さてと…
※この投稿は、2015/10/22現在の公開情報を元に記載しています。最新の情報がないか確認してくださいね。
IoT Hubのプレビューが9/30に公開されました。タイミング的にちょっと遅い気もしますが、Raspberry Pi2で動作するWindows 10 IoT CoreデバイスをIoT Hubに接続する方法を説明します。とりあえず試す方法が、https://azure.microsoft.com/ja-jp/documentation/articles/iot-hub-csharp-csharp-getstarted/ に書かれています。この説明は、.NET Framework上でC#を使ったサンプルで、Windows 10 IoT Coreの場合はWindows RT上で動くUniversal Windows Applicationという違いがあって、そのままでは、動かすことができません。更に、プレビュー状態のため、説明通りにいかない部分もあるので、ここで解説しておきます。
※IoT Hubにつなぐ部分、送受信する部分は、通常のWindows 10のUWAでも利用できます。
折角なので、加速度センサー(ADXL345)、温度センサー(BME280)を使ってIoT Hubに送ってみましょう。基本の部分は、https://doc.co/4dEWrJを見てください。ハードウェアの構成は、このドキュメントに記載の
これに加えて、LEDも追加します。
次に、センサーやLEDをI2CやGPIOで制御するために、IoT Extensionを参照に加えます。
そして、https://doc.co/4dEWrJに記載に従って、MainPage.xaml.csのProgramクラスにdeviceId(Guid値)と、プロジェクトにSensorクラスを追加してください。
次は、IoT Hubへのアクセス用ライブラリの追加です。本来ならNuGetを使ってインストールできるのですが、本投稿を書いている時点でははうまくいかないので、ブラウザで、
https://github.com/Azure/azure-iot-sdks/
を開き、表示されたページの右横にある、”Download ZIP”をクリックし、どこかにZIPファイルを保存、ZIPファイルのプロパティでブロックを外し(必ずやってくだいね)、適当な場所に保存します。
csharp/Microsoft.Azure.Devices.Client.WinRTに入っているMicrosoft.Azure.Devices.Client.WinRT.csprojをプロジェクトに参照追加します。
ソリューションエクスプローラーで、ソリューションを右クリックし、”追加”→”既存のプロジェクト”を選択し、Microsoft.Azure.Devices.Client.WinRT.csproj を選択し、ソリューションに追加してください。
次に、追加したSDKのプロジェクトを、作成中のプロジェクト参照として追加します。これで、NuGetで組み込んだのと同じ状態になります。
これで、IoT Hubにアクセスする準備は完了です。
後は、Modelsという名前でフォルダーをプロジェクトに作成し、そのフォルダーにSensorReadingという名前でクラスを作成し、
public class SensorReading
{
public string deviceId { get; set; }
public double temp { get; set; }
public double accelx { get; set; }
public double accely { get; set; }
public double accelz { get; set; }
public DateTime time { get; set; }
public string msgId { get; set; }
}
と、コーディングしてください。このクラスを使ってセンサー計測値をJSONに変換します。
さて、MainPage.xaml.csのnamespaceの中身を以下の様に編集します。
namespace Win10IoTDevice
{
using Windows.Devices.Gpio;
using Microsoft.Azure.Devices.Client;
using System.Threading.Tasks;
using System.Diagnostics;
using Newtonsoft.Json;
using System.Text;
/// <summary>
/// それ自体で使用できる空白ページまたはフレーム内に移動できる空白ページ。
/// </summary>
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
this.Loaded += MainPage_Loaded;
}
Guid deviceId = new Guid(/* your device guid */);
static string iotHubUri = "[your iot hub].azure-devices.net";
static string deviceKey = "[your device key for your device guid]";
private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
InitLEDGPIO();
InitializeSensor();
InitializeUploadIoTHub();
}
DeviceClient deviceClient;
DispatcherTimer uploadTimer;
#pragma warning disable 4014
private void InitializeUploadIoTHub()
{
try
{
deviceClient = DeviceClient.Create(iotHubUri, AuthenticationMethodFactory.CreateAuthenticationWithRegistrySymmetricKey(deviceId.ToString(), deviceKey), TransportType.Http1);
uploadTimer = new DispatcherTimer();
uploadTimer.Interval = TimeSpan.FromMinutes(2);
uploadTimer.Tick += UploadTimer_Tick;
uploadTimer.Start();
ReceiveCommands();
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
private async void UploadTimer_Tick(object sender, object e)
{
uploadTimer.Stop();
await SendEvent();
uploadTimer.Start();
}
async Task SendEvent()
{
List<SensorReadingBuffer> currentReadings = new List<SensorReadingBuffer>();
lock (this)
{
foreach (var r in lastSensorReading)
{
currentReadings.Add(new SensorReadingBuffer()
{
AccelX = r.AccelX,
AccelY = r.AccelY,
AccelZ = r.AccelZ,
Temperature = r.Temperature,
Timestamp = r.Timestamp
});
}
lastSensorReading.Clear();
}
Debug.WriteLine("Device sending {0} messages to IoTHub...\n", currentReadings.Count);
try
{
List<Models.SensorReading> sendingBuffers = new List<Models.SensorReading>();
for (int count = 0; count < currentReadings.Count; count++)
{
var sensorReading = new Models.SensorReading()
{
msgId = deviceId.ToString() + currentReadings[count].Timestamp.ToString("yyyyMMddhhmmssfff"),
accelx = currentReadings[count].AccelX,
accely = currentReadings[count].AccelY,
accelz = currentReadings[count].AccelZ,
deviceId = deviceId.ToString(),
temp = currentReadings[count].Temperature,
time = currentReadings[count].Timestamp
};
sendingBuffers.Add(sensorReading);
}
var payload = JsonConvert.SerializeObject(sendingBuffers);
Message eventMessage = new Message(Encoding.UTF8.GetBytes(payload));
Debug.WriteLine("\t{0}> Sending message: {1}, Data: [{2}]", DateTime.Now.ToLocalTime(), currentReadings.Count, payload);
await deviceClient.SendEventAsync(eventMessage);
}
catch (Exception ex)
{
Debug.Write(ex.Message);
}
}
async Task ReceiveCommands()
{
Debug.WriteLine("\nDevice waiting for commands from IoTHub...\n");
Message receivedMessage;
string messageData;
while (true)
{
receivedMessage = await deviceClient.ReceiveAsync();
if (receivedMessage != null)
{
messageData = Encoding.ASCII.GetString(receivedMessage.GetBytes());
Debug.WriteLine("\t{0}> Received message: {1}", DateTime.Now.ToLocalTime(), messageData);
var command = messageData.ToLower();
if (command.StartsWith("gpio:"))
{
var order = command.Split(new char[] { ':' });
switch (order[1])
{
case "0":
LedControl(0);
break;
case "1":
LedControl(1);
break;
case "2":
LedControl(2);
break;
}
}
await deviceClient.CompleteAsync(receivedMessage);
}
await Task.Delay(TimeSpan.FromSeconds(10));
}
}
#pragma warning restore 4014
IoTDevice.IoTKitHoLSensor mySensor;
DispatcherTimer measureTimer;
private void InitializeSensor()
{
mySensor = IoTDevice.IoTKitHoLSensor.GetCurrent(IoTDevice.IoTKitHoLSensor.TemperatureSensor.BME280);
lastSensorReading = new List<SensorReadingBuffer>();
measureTimer = new DispatcherTimer();
measureTimer.Interval = TimeSpan.FromMilliseconds(1000);
measureTimer.Tick += MeasureTimer_Tick;
measureTimer.Start();
}
private void MeasureTimer_Tick(object sender, object e)
{
var reading = mySensor.TakeMeasurement();
lock (this)
{
lastSensorReading.Add(new SensorReadingBuffer()
{
AccelX = reading.AccelX,
AccelY = reading.AccelY,
AccelZ = reading.AccelZ,
Temperature = reading.Temperature,
Timestamp = DateTime.Now
});
}
}
List<SensorReadingBuffer> lastSensorReading;
void LedControl(int onLedId)
{
for (int i = 0; i < LED_PIN.Length; i++)
{
ledPin[i].Write(GpioPinValue.High);
}
ledPin[onLedId].Write(GpioPinValue.Low);
}
GpioPin[] ledPin = new GpioPin[3];
int[] LED_PIN = { 5, 6, 13 };
// GPIO5 - 29
// GPIO6 - 31
// GPIO13 - 33
// 5V - 2
private void InitLEDGPIO()
{
var gpio = GpioController.GetDefault();
for (int i = 0; i < LED_PIN.Length; i++)
{
ledPin[i] = gpio.OpenPin(LED_PIN[i]);
if (ledPin[i] != null)
{
ledPin[i].Write(GpioPinValue.High);
ledPin[i].SetDriveMode(GpioPinDriveMode.Output);
}
}
}
}
class SensorReadingBuffer
{
public double Temperature { get; set; }
public double AccelX { get; set; }
public double AccelY { get; set; }
public double AccelZ { get; set; }
public DateTime Timestamp { get; set; }
}
}
コードの中で、deviceId、iotHubUri、deviceKeyという変数がありますが、まずはGuid生成ツールで新規にGuidを作成して設定し、
https://azure.microsoft.com/ja-jp/documentation/articles/iot-hub-csharp-csharp-getstarted/
に記載のCreateDeviceEntryアプリでDevice Keyを作成して、deviceKeyの値とし、作成したIoT HubのURLにあわせてiotHubUri変数を編集すれば動きます。チュートリアルと上のコードを見比べてみてください。IoT Hubとの送受信の部分は基本同一です。
1秒ごとにセンサー計測値を貯めて、2分ごとにIoT Hubに貯めたデータを一括送信します。チュートリアルのReadDeviceToCloudMessagesを起動しておくと送信したデータを確認できます。また、
https://azure.microsoft.com/ja-jp/documentation/articles/iot-hub-csharp-csharp-c2d/
に記載の、SendCloudToDeviceのSendCloudToDeviceMessageAsyncメソッドを
private async static Task SendCloudToDeviceMessageAsync(string command)
{
var commandMessage =new Message(Encoding.ASCII.GetBytes(command));
await serviceClient.SendAsync("device id guid", commandMessage);
}
と変更し、7のMainメソッドを
Console.WriteLine("Send Cloud-to-Device message\n");
serviceClient =ServiceClient.CreateFromConnectionString(connectionString);
Console.WriteLine("Press any key to send a C2D message.");
var string command = Console.ReadLine();
SendCloudToDeviceMessageAsync(command).Wait();
Console.ReadLine();
と変更して、実行して、コンソールで、gpio:0、とか、gpio:1とか、gpio:2とか入力すると、Raspberry Pi2側でデータを受信し、対応するLED(0の場合はGPIO5、1の場合はGPIO6、2の場合はGPIO13に、つながっているLEDが光ります。
以上、かなりはしょって説明しましたが、機材が手元にある人はやってみてくださいね。まだ、プレビューなので、結構落ちたりしますが、フィードバックもよろしくお願いします。
安定したころを見計らって、https://doc.co/M7uGBDにIoT Hubシナリオを追加予定です。
新しい技術を獲得する唯一の早道は、実際にやってみることです。是非チャレンジしてみてくださいね