diff --git a/M2Mqtt.sln b/M2Mqtt.sln
index fc3baeaf..a88b7411 100644
--- a/M2Mqtt.sln
+++ b/M2Mqtt.sln
@@ -1,48 +1,59 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 2013
-VisualStudioVersion = 12.0.31101.0
+# Visual Studio 15
+VisualStudioVersion = 15.0.27004.2009
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "M2Mqtt.Net", "M2Mqtt\M2Mqtt.Net.csproj", "{A11AEF5A-B246-4FE8-8330-06DB73CC8074}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "M2Mqtt.NetCf39", "M2Mqtt\M2Mqtt.NetCf39.csproj", "{BB9B7FF4-6502-41AF-8851-5060B67645E8}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "M2Mqtt.NetMf42", "M2Mqtt\M2Mqtt.NetMf42.csproj", "{F733523A-F14E-4F5A-9E7C-085CA80F52B1}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "M2Mqtt.NetMf43", "M2Mqtt\M2Mqtt.NetMf43.csproj", "{6A6D540B-8554-4FFD-8884-8BEFCCD9AD41}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "M2Mqtt.WinRT", "M2Mqtt\M2Mqtt.WinRT.csproj", "{0238F0E3-A02B-428D-8A3F-410D8F15BB50}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "M2Mqtt.Universal", "M2Mqtt\M2Mqtt.Universal.csproj", "{7E4BD745-3633-44FE-B4DD-40F3350D66D2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
+ Debug|ARM = Debug|ARM
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
+ Release|ARM = Release|ARM
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Debug|ARM.ActiveCfg = Debug|Any CPU
+ {A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Debug|ARM.Build.0 = Debug|Any CPU
+ {A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Debug|x64.Build.0 = Debug|Any CPU
+ {A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Debug|x86.Build.0 = Debug|Any CPU
{A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Release|Any CPU.Build.0 = Release|Any CPU
- {BB9B7FF4-6502-41AF-8851-5060B67645E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {BB9B7FF4-6502-41AF-8851-5060B67645E8}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {BB9B7FF4-6502-41AF-8851-5060B67645E8}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
- {BB9B7FF4-6502-41AF-8851-5060B67645E8}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {BB9B7FF4-6502-41AF-8851-5060B67645E8}.Release|Any CPU.Build.0 = Release|Any CPU
- {BB9B7FF4-6502-41AF-8851-5060B67645E8}.Release|Any CPU.Deploy.0 = Release|Any CPU
- {F733523A-F14E-4F5A-9E7C-085CA80F52B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {F733523A-F14E-4F5A-9E7C-085CA80F52B1}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {F733523A-F14E-4F5A-9E7C-085CA80F52B1}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {F733523A-F14E-4F5A-9E7C-085CA80F52B1}.Release|Any CPU.Build.0 = Release|Any CPU
- {6A6D540B-8554-4FFD-8884-8BEFCCD9AD41}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {6A6D540B-8554-4FFD-8884-8BEFCCD9AD41}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {6A6D540B-8554-4FFD-8884-8BEFCCD9AD41}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {6A6D540B-8554-4FFD-8884-8BEFCCD9AD41}.Release|Any CPU.Build.0 = Release|Any CPU
- {0238F0E3-A02B-428D-8A3F-410D8F15BB50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {0238F0E3-A02B-428D-8A3F-410D8F15BB50}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {0238F0E3-A02B-428D-8A3F-410D8F15BB50}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {0238F0E3-A02B-428D-8A3F-410D8F15BB50}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Release|ARM.ActiveCfg = Release|Any CPU
+ {A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Release|ARM.Build.0 = Release|Any CPU
+ {A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Release|x64.ActiveCfg = Release|Any CPU
+ {A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Release|x64.Build.0 = Release|Any CPU
+ {A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Release|x86.ActiveCfg = Release|Any CPU
+ {A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Release|x86.Build.0 = Release|Any CPU
+ {7E4BD745-3633-44FE-B4DD-40F3350D66D2}.Debug|Any CPU.ActiveCfg = Debug|x86
+ {7E4BD745-3633-44FE-B4DD-40F3350D66D2}.Debug|ARM.ActiveCfg = Debug|ARM
+ {7E4BD745-3633-44FE-B4DD-40F3350D66D2}.Debug|ARM.Build.0 = Debug|ARM
+ {7E4BD745-3633-44FE-B4DD-40F3350D66D2}.Debug|x64.ActiveCfg = Debug|x64
+ {7E4BD745-3633-44FE-B4DD-40F3350D66D2}.Debug|x64.Build.0 = Debug|x64
+ {7E4BD745-3633-44FE-B4DD-40F3350D66D2}.Debug|x86.ActiveCfg = Debug|x86
+ {7E4BD745-3633-44FE-B4DD-40F3350D66D2}.Debug|x86.Build.0 = Debug|x86
+ {7E4BD745-3633-44FE-B4DD-40F3350D66D2}.Release|Any CPU.ActiveCfg = Release|x86
+ {7E4BD745-3633-44FE-B4DD-40F3350D66D2}.Release|ARM.ActiveCfg = Release|ARM
+ {7E4BD745-3633-44FE-B4DD-40F3350D66D2}.Release|ARM.Build.0 = Release|ARM
+ {7E4BD745-3633-44FE-B4DD-40F3350D66D2}.Release|x64.ActiveCfg = Release|x64
+ {7E4BD745-3633-44FE-B4DD-40F3350D66D2}.Release|x64.Build.0 = Release|x64
+ {7E4BD745-3633-44FE-B4DD-40F3350D66D2}.Release|x86.ActiveCfg = Release|x86
+ {7E4BD745-3633-44FE-B4DD-40F3350D66D2}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {B0CADC1E-0867-4C86-A1E6-1D3FABF29ACA}
+ EndGlobalSection
EndGlobal
diff --git a/M2Mqtt/IMqttNetworkChannel.cs b/M2Mqtt/IMqttNetworkChannel.cs
index f4ff90fb..1149ce69 100644
--- a/M2Mqtt/IMqttNetworkChannel.cs
+++ b/M2Mqtt/IMqttNetworkChannel.cs
@@ -51,6 +51,10 @@ public interface IMqttNetworkChannel
/// Number of byte sent
int Send(byte[] buffer);
+#if WINDOWS_UWP
+ System.Threading.Tasks.Task SendAsync(byte[] buffer);
+#endif
+
///
/// Close the network channel
///
@@ -60,10 +64,14 @@ public interface IMqttNetworkChannel
/// Connect to remote server
///
void Connect();
-
+
///
/// Accept client connection
///
void Accept();
+
+#if WINDOWS_UWP
+ System.Threading.Tasks.Task ConnectAsync();
+#endif
}
-}
+}
\ No newline at end of file
diff --git a/M2Mqtt/M2Mqtt.Universal.csproj b/M2Mqtt/M2Mqtt.Universal.csproj
new file mode 100644
index 00000000..f81f2c0c
--- /dev/null
+++ b/M2Mqtt/M2Mqtt.Universal.csproj
@@ -0,0 +1,154 @@
+
+
+
+
+ Debug
+ x86
+ {7E4BD745-3633-44FE-B4DD-40F3350D66D2}
+ Library
+ Properties
+ uPLibrary.Networking.M2Mqtt
+ M2Mqtt.Universal
+ en-US
+ UAP
+ 10.0.15063.0
+ 10.0.14393.0
+ 14
+ 512
+ {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+
+
+ x86
+ true
+ ..\bin\x86\Debug\M2Mqtt.Universal\
+ TRACE;DEBUG;NETFX_CORE;WINDOWS_UWP;WINDOWS_APP;SSL
+ ;2008
+ full
+ x86
+ false
+ prompt
+
+
+ x86
+ ..\bin\x86\Release\M2Mqtt.Universal\
+ TRACE;NETFX_CORE;WINDOWS_UWP;WINDOWS_APP;SSL
+ true
+ ;2008
+ pdbonly
+ x86
+ false
+ prompt
+
+
+ ARM
+ true
+ ..\bin\ARM\Debug\M2Mqtt.Universal\
+ DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP;WINDOWS_APP;SSL
+ ;2008
+ full
+ ARM
+ false
+ prompt
+
+
+ ARM
+ ..\bin\ARM\Release\M2Mqtt.Universal\
+ TRACE;NETFX_CORE;WINDOWS_UWP;WINDOWS_APP;SSL
+ true
+ ;2008
+ pdbonly
+ ARM
+ false
+ prompt
+
+
+ x64
+ true
+ ..\bin\x64\Debug\M2Mqtt.Universal\
+ DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP;WINDOWS_APP;SSL
+ ;2008
+ full
+ x64
+ false
+ prompt
+
+
+ x64
+ ..\bin\x64\Release\M2Mqtt.Universal\
+ TRACE;NETFX_CORE;WINDOWS_UWP;WINDOWS_APP;SSL
+ true
+ ;2008
+ pdbonly
+ x64
+ false
+ prompt
+
+
+ PackageReference
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 5.4.0
+
+
+
+ 14.0
+
+
+
+
\ No newline at end of file
diff --git a/M2Mqtt/MqttClient.cs b/M2Mqtt/MqttClient.cs
index 40745b5e..3dadd826 100644
--- a/M2Mqtt/MqttClient.cs
+++ b/M2Mqtt/MqttClient.cs
@@ -167,7 +167,7 @@ public class MqttClient
// event for peer/client disconnection
public event ConnectionClosedEventHandler ConnectionClosed;
-
+
// channel to communicate over the network
private IMqttNetworkChannel channel;
@@ -304,7 +304,7 @@ public MqttClient(string brokerHostName) :
/// Client certificate
public MqttClient(string brokerHostName, int brokerPort, bool secure, X509Certificate caCert, X509Certificate clientCert, MqttSslProtocols sslProtocol)
#else
- public MqttClient(string brokerHostName, int brokerPort, bool secure, MqttSslProtocols sslProtocol)
+ public MqttClient(string brokerHostName, int brokerPort, bool secure, MqttSslProtocols sslProtocol)
#endif
{
#if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK || WINDOWS_APP || WINDOWS_PHONE_APP)
@@ -476,9 +476,16 @@ private void Init(string brokerHostName, int brokerPort, bool secure, X509Certif
///
/// Client identifier
/// Return code of CONNACK message from broker
+#if WINDOWS_UWP
+ public async System.Threading.Tasks.Task ConnectAsync(string clientId)
+ {
+
+ return await this.ConnectAsync(clientId, null, null, false, MqttMsgConnect.QOS_LEVEL_AT_MOST_ONCE, false, null, null, true, MqttMsgConnect.KEEP_ALIVE_PERIOD_DEFAULT);
+#else
public byte Connect(string clientId)
{
return this.Connect(clientId, null, null, false, MqttMsgConnect.QOS_LEVEL_AT_MOST_ONCE, false, null, null, true, MqttMsgConnect.KEEP_ALIVE_PERIOD_DEFAULT);
+#endif
}
///
@@ -488,11 +495,15 @@ public byte Connect(string clientId)
/// Username
/// Password
/// Return code of CONNACK message from broker
- public byte Connect(string clientId,
- string username,
- string password)
+#if WINDOWS_UWP
+ public async System.Threading.Tasks.Task ConnectAsync(string clientId, string username, string password)
+ {
+ return await this.ConnectAsync(clientId, username, password, false, MqttMsgConnect.QOS_LEVEL_AT_MOST_ONCE, false, null, null, true, MqttMsgConnect.KEEP_ALIVE_PERIOD_DEFAULT);
+#else
+ public byte Connect(string clientId, string username, string password)
{
return this.Connect(clientId, username, password, false, MqttMsgConnect.QOS_LEVEL_AT_MOST_ONCE, false, null, null, true, MqttMsgConnect.KEEP_ALIVE_PERIOD_DEFAULT);
+#endif
}
///
@@ -504,13 +515,15 @@ public byte Connect(string clientId,
/// Clean sessione flag
/// Keep alive period
/// Return code of CONNACK message from broker
- public byte Connect(string clientId,
- string username,
- string password,
- bool cleanSession,
- ushort keepAlivePeriod)
+#if WINDOWS_UWP
+ public async System.Threading.Tasks.Task ConnectAsync(string clientId, string username, string password, bool cleanSession, ushort keepAlivePeriod)
+ {
+ return await this.ConnectAsync(clientId, username, password, false, MqttMsgConnect.QOS_LEVEL_AT_MOST_ONCE, false, null, null, cleanSession, keepAlivePeriod);
+#else
+ public byte Connect(string clientId, string username, string password, bool cleanSession, ushort keepAlivePeriod)
{
return this.Connect(clientId, username, password, false, MqttMsgConnect.QOS_LEVEL_AT_MOST_ONCE, false, null, null, cleanSession, keepAlivePeriod);
+#endif
}
///
@@ -527,16 +540,11 @@ public byte Connect(string clientId,
/// Clean sessione flag
/// Keep alive period
/// Return code of CONNACK message from broker
- public byte Connect(string clientId,
- string username,
- string password,
- bool willRetain,
- byte willQosLevel,
- bool willFlag,
- string willTopic,
- string willMessage,
- bool cleanSession,
- ushort keepAlivePeriod)
+#if WINDOWS_UWP
+ public async System.Threading.Tasks.Task ConnectAsync(string clientId, string username, string password, bool willRetain, byte willQosLevel, bool willFlag, string willTopic, string willMessage, bool cleanSession, ushort keepAlivePeriod)
+#else
+ public byte Connect(string clientId, string username, string password, bool willRetain, byte willQosLevel, bool willFlag, string willTopic, string willMessage, bool cleanSession, ushort keepAlivePeriod)
+#endif
{
// create CONNECT message
MqttMsgConnect connect = new MqttMsgConnect(clientId,
@@ -553,8 +561,12 @@ public byte Connect(string clientId,
try
{
+#if WINDOWS_UWP
+ await this.channel.ConnectAsync();
+#else
// connect to the broker
this.channel.Connect();
+#endif
}
catch (Exception ex)
{
@@ -566,7 +578,7 @@ public byte Connect(string clientId,
this.isConnectionClosing = false;
// start thread for receiving messages from broker
Fx.StartThread(this.ReceiveThread);
-
+
MqttMsgConnack connack = (MqttMsgConnack)this.SendReceive(connect);
// if connection accepted, start keep alive timer and
if (connack.ReturnCode == MqttMsgConnack.CONN_ACCEPTED)
@@ -593,7 +605,7 @@ public byte Connect(string clientId,
// start thread for raising received message event from broker
Fx.StartThread(this.DispatchEventThread);
-
+
// start thread for handling inflight messages queue to broker asynchronously (publish and acknowledge)
Fx.StartThread(this.ProcessInflightThread);
@@ -1000,7 +1012,12 @@ private void Send(byte[] msgBytes)
try
{
// send message
+#if WINDOWS_UWP
+ var task = this.channel.SendAsync(msgBytes);
+ task.GetAwaiter().GetResult();
+#else
this.channel.Send(msgBytes);
+#endif
#if !BROKER
// update last message sent ticks
@@ -1052,7 +1069,12 @@ private MqttMsgBase SendReceive(byte[] msgBytes, int timeout)
try
{
// send message
+#if WINDOWS_UWP
+ var task = this.channel.SendAsync(msgBytes);
+ task.GetAwaiter().GetResult();
+#else
this.channel.Send(msgBytes);
+#endif
// update last message sent ticks
this.lastCommTime = Environment.TickCount;
diff --git a/M2Mqtt/Net/MqttNetworkChannel.cs b/M2Mqtt/Net/MqttNetworkChannel.cs
index 94a329d6..c7843263 100644
--- a/M2Mqtt/Net/MqttNetworkChannel.cs
+++ b/M2Mqtt/Net/MqttNetworkChannel.cs
@@ -26,6 +26,7 @@ Paolo Patierno - initial API and implementation and/or initial documentation
using System.Net;
using System.Security.Cryptography.X509Certificates;
using System;
+using System.Threading.Tasks;
namespace uPLibrary.Networking.M2Mqtt
{
diff --git a/M2Mqtt/Properties/AssemblyInfo.cs b/M2Mqtt/Properties/AssemblyInfo.cs
index b218618f..a9c24a64 100644
--- a/M2Mqtt/Properties/AssemblyInfo.cs
+++ b/M2Mqtt/Properties/AssemblyInfo.cs
@@ -41,4 +41,5 @@ Paolo Patierno - initial API and implementation and/or initial documentation
// to avoid compilation error (AssemblyFileVersionAttribute doesn't exist) under .Net CF 3.5
#if !WindowsCE
[assembly: AssemblyFileVersion("4.3.0.0")]
-#endif
\ No newline at end of file
+#endif
+[assembly: ComVisible(false)]
\ No newline at end of file
diff --git a/M2Mqtt/Properties/M2Mqtt.Universal.rd.xml b/M2Mqtt/Properties/M2Mqtt.Universal.rd.xml
new file mode 100644
index 00000000..d65f60b9
--- /dev/null
+++ b/M2Mqtt/Properties/M2Mqtt.Universal.rd.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
diff --git a/M2Mqtt/WinRT/MqttNetworkChannel.cs b/M2Mqtt/WinRT/MqttNetworkChannel.cs
index 305992c1..fb171475 100644
--- a/M2Mqtt/WinRT/MqttNetworkChannel.cs
+++ b/M2Mqtt/WinRT/MqttNetworkChannel.cs
@@ -24,6 +24,8 @@ Paolo Patierno - initial API and implementation and/or initial documentation
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Storage.Streams;
using System.Threading;
+using Windows.Security.Cryptography.Certificates;
+using System.Diagnostics;
namespace uPLibrary.Networking.M2Mqtt
{
@@ -130,6 +132,14 @@ public int Send(byte[] buffer)
return (int)this.socket.OutputStream.WriteAsync(buffer.AsBuffer()).AsTask().Result;
}
+#if WINDOWS_UWP
+ public async Task SendAsync(byte[] buffer)
+ {
+ var result = await this.socket.OutputStream.WriteAsync(buffer.AsBuffer());
+ return (int)result;
+ }
+#endif
+
public void Close()
{
this.socket.Dispose();
@@ -145,6 +155,51 @@ public void Connect()
MqttSslUtility.ToSslPlatformEnum(this.sslProtocol)).AsTask().Wait();
}
+ public async Task ConnectAsync()
+ {
+ this.socket = new StreamSocket();
+ int retryTime = 2;
+ while (retryTime > 0)
+ {
+ try
+ {
+ await socket.ConnectAsync(this.remoteHostName, this.remotePort.ToString(), MqttSslUtility.ToSslPlatformEnum(this.sslProtocol));
+ break;
+ }
+ catch (Exception exception)
+ {
+ // If this is an unknown status it means that the error is fatal and retry will likely fail.
+ if (SocketError.GetStatus(exception.HResult) == SocketErrorStatus.Unknown)
+ {
+ throw;
+ }
+
+ // If the exception was caused by an SSL error that is ignorable we are going to prompt the user
+ // with an enumeration of the errors and ask for permission to ignore.
+ if (socket.Information.ServerCertificateErrorSeverity != SocketSslErrorSeverity.Ignorable)
+ {
+ Debug.WriteLine("Connect failed with error: " + exception.Message);
+ throw;
+ }
+#if DEBUG
+ // -----------------------------------------------------------------------------------------------
+ // WARNING: Only test applications should ignore SSL errors.
+ // In real applications, ignoring server certificate errors can lead to Man-In-The-Middle attacks.
+ // -----------------------------------------------------------------------------------------------
+
+ socket.Control.IgnorableServerCertificateErrors.Clear();
+
+ foreach (var ignorableError in socket.Information.ServerCertificateErrors)
+ {
+ socket.Control.IgnorableServerCertificateErrors.Add(ignorableError);
+ }
+#endif
+ }
+
+ retryTime -= 1;
+ }
+ }
+
public void Accept()
{
// TODO : SSL support with StreamSocket / StreamSocketListener seems to be NOT supported
@@ -176,4 +231,4 @@ public static SocketProtectionLevel ToSslPlatformEnum(MqttSslProtocols mqttSslPr
}
}
}
-}
+}
\ No newline at end of file
diff --git a/M2Mqtt/packages.config b/M2Mqtt/packages.config
new file mode 100644
index 00000000..ee51c237
--- /dev/null
+++ b/M2Mqtt/packages.config
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/M2Mqtt/project.json b/M2Mqtt/project.json
new file mode 100644
index 00000000..091353e9
--- /dev/null
+++ b/M2Mqtt/project.json
@@ -0,0 +1,9 @@
+{
+ "framework": {
+ "net451": {
+ "dependencies": {
+ "System.Collections.Immutable": "1.3.1"
+ }
+ }
+ }
+}
\ No newline at end of file