微軟的IoT Hub提供了許多IoT客戶端裝置作為訊息接收用的服務
不過使用IoT Hub有著一些限制,像是僅能使用HTTP、AMQP、MQTT這三種通訊協定進行訊息的發送
以及裝置中必須要能夠將Key值壓上Timespan,作為傳入IoT Hub訊息的識別
以目前一般僅有少量ROM的裝置來說,根本無法將微軟的Azure IoT SDK塞進裝置中,這時就只能透過WebApp作為轉送的中繼站,處理訊息轉送進IoT Hub的動作了
前提是,客戶端裝置可以透過修改程式的方式,將訊息送至WebAPI上,不過我相信透過程式碼的修改,目前絕大多數的客戶端裝置應該都可以達到這樣的功能
製作這樣的WebApp作為客戶端轉發的功能不難,但是相對的會多出一些工必須製作,像是必須要先將客戶端裝置的Id與Key先存在資料庫中等等
下面會將步驟完整的說明,當然,若是對系統架構較為熟悉的人應該很快就能上手了
首先,先建立一個資料庫,並加入一個DeviceFile的資料表,該資料表包含了DeviceId與DeviceKey兩個欄位
接著將在IoT Hub上,要透過WebApp轉送訊息的裝置編號與Key值寫進該資料庫中
這樣資料庫的準備動作就完成了,接著開啟Visual Studio,並建立一個新的Azure API App專案
在WebAPI的專案中,先加入[Microsoft.Azure.Devices.Client]與[Microsoft.Azure.Devices]兩個Nuget套件
接著在Models的資料夾下,建立一個Message.cs的類別庫,並將下面程式碼加入至該類別庫中
public class Message
{
public string DeviceId { get; set; }
public string MessageContent { get; set; }
}
這個類別庫主要是在定義要從裝置端透過POST方法傳送訊息至WebApp時的JSON物件格式,當然實際開發的情況可以視需要進行修改,在這裡,我們只將裝置代碼與訊息從客戶端裝置傳送至WebApp上
接著在Controllers的資料夾中,加入[MessagesController]這個控制器
將下面的程式碼放入至MessagesController.cs這一個控制器的程式碼中
static string iotHubUri = "[在這裡放入IoT Hub的Url]";
/// <summary>
/// 發送訊息至IoT Hub
/// </summary>
/// <param name="value">發送物件</param>
/// <returns></returns>
public async Task<HttpResponseMessage> Post([FromBody]Models.Message value)
{
HttpStatusCode code = HttpStatusCode.OK;
string strContent = "true";
try
{
// 透過EF找出資料庫中該裝置的Key值
Models.DeviceFile objDevice = new Models.IoTModel().DeviceFile.FirstOrDefault(x => x.DeviceId == value.DeviceId);
if (objDevice != null)
{
string strDeviceKey = objDevice.DeviceKey;
// 傳送訊息進入IoT Hub
DeviceClient deviceClient = null;
deviceClient = DeviceClient.CreateFromConnectionString
(
$"HostName={iotHubUri};DeviceId={objDevice.DeviceId};SharedAccessKey={objDevice.DeviceKey}",
Microsoft.Azure.Devices.Client.TransportType.Amqp
);
await deviceClient.SendEventAsync(new Microsoft.Azure.Devices.Client.Message(Encoding.UTF8.GetBytes(value.MessageContent)));
}
else
{
code = HttpStatusCode.NotFound;
strContent = "找不到該裝置,該裝置並無登錄於資料庫中";
}
}
catch (Exception e)
{
code = HttpStatusCode.BadRequest;
strContent = e.Message;
}
return Request.CreateResponse(code, strContent);
}
這一段程式碼主要的功能在,透過Entity Framework的方式,將客戶端裝置傳入的裝置代碼,找出對應的DeviceKey值,並將訊息轉送至IoT Hub中。整段程式碼其實不複雜,就只是很單純的取出Key值並轉送進IoT Hub中
當程式碼製作完成後進行測試一下,看是否能夠執行從上圖Swagger的測試畫面來看,Messages的控制器已經收到測試畫面送入的訊息,也回傳測試成功的訊息
接下來,就必須在客戶端的裝置,加上POST的副程式,將訊息送至WebAPI上,在這裡,我將模擬器的程式碼作了一些修改,並將POST訊息的功能加上去。
首先在模擬器上,加入一個要POST到WebAPI的按鈕
接著將下面的程式碼,加入至ButtonClick事件與表單程式碼中
/// <summary>
/// 將訊息送出至WebAPI的動作
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSendToWebAPI_Click(object sender, EventArgs e)
{
// 因為只要送到WebAPI,所以只要訊息跟裝置代碼就夠了
MessageModel objMsg = new MessageModel()
{
DeviceId = txtDeviceId.Text,
MessageContent = txtMessage.Text,
};
// 放在網路上的WebApp的Url
string strUrl = "[WebAPI的Url]";
// 送出至WebAPI
HttpStatusCode code = HttpStatusCode.OK;
string strResponse = CallAPI(strUrl, "POST", JsonConvert.SerializeObject(objMsg) , out code);
if (code == HttpStatusCode.OK)
{
MessageBox.Show("發送完成");
}
else
{
MessageBox.Show("發送失敗," + strResponse);
}
}
protected string CallAPI(string strUrl, string strHttpMethod, string strPostContent, out HttpStatusCode code)
{
HttpWebRequest request = HttpWebRequest.Create(strUrl) as HttpWebRequest;
request.Method = strHttpMethod;
code = HttpStatusCode.OK;
if (strPostContent != "" && strPostContent != string.Empty)
{
request.KeepAlive = true;
request.ContentType = "application/json";
byte[] bs = Encoding.ASCII.GetBytes(strPostContent);
Stream reqStream = request.GetRequestStream();
reqStream.Write(bs, 0, bs.Length);
}
string strReturn = "";
try
{
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
var respStream = response.GetResponseStream();
strReturn = new StreamReader(respStream).ReadToEnd();
}
catch (Exception e)
{
strReturn = e.Message;
code = HttpStatusCode.NotFound;
}
return strReturn;
}
這段程式碼主要的目的,就是當按下送出至WebAPI的按鈕時,會將裝置代碼與訊息轉換成MessageModel的物件JSON字串,再透過CallAPI這一個副程式將訊息POST到WebAPI上並顯示成功或是失敗的訊息
程式碼寫好後,實際執行看看結果,可以得到訊息確實送進IoT Hub了
IoT Hub也有確實收到訊息,這邊有兩筆訊息,分別是透過Swagger測試傳入的訊息,以及透過模擬器送入的訊息,所以總數為2筆
以目前的IoT裝置來說,由於壓低成本的關係,所以裝置本身無法包含IoT SDK的所有功能,但是透過WebApp進行IoT訊息轉送的設計,可以先解決老舊裝置無法直接支援Azure IoT Hub的問題