はじめに
この記事は、Raspbery Pi に FCM から Push 通知し、LED を点灯する Android Things のサンプルを Xamarin を使って作成したときのメモです。
年末で比較的時間が取れる状況だったので、まったくやったことがないハードウェア周りのことにトライしてみました。(初めての電子工作でした…)
準備したもの
- ハードウェア
- Raspbery Pi Model 3
- Android Things をインストール済み
- Raspbery Pi に Android Things をインストールする手順は、ここを参考にしました
- Android Things をインストール済み
- ブレッドボード(1個)
- LED(1個)
- 抵抗器(1個)
- ジャンパーワイヤー(2本、片方だけメスのもの)
- Raspbery Pi Model 3
- ソフトウェア
- Visual Studio 2017
- Xamarin.Forms で実装
-
Xamarin.Android.Things
- Android Things を Native Binding した NuGet パッケージ
-
Firebase Push Notification Plugin for Xamarin iOS and Android
- FCM 関連のライブラリ
-
Fiddler
- FCM のメッセージ送信用に利用
- Visual Studio 2017
サンプルコード
今回のサンプルを GitHub に置いておきました。
配線図
ハードウェアの配線図は以下の通りです。
この図は、Fritzing を使って作成しました。
アプリケーションの作成
0. Solution 構成
サンプルのソリューション構成を以下のスクリーンショットに示します。
-
Push.Sample プロジェクト
主なファイル 概要 IBlinkLedService.cs LED を点灯させるためのインタフェース ILogService.cs 簡易ログ出力用のインターフェース MainPage.xaml.cs FCM メッセージ受信後 LED を点灯する処理を呼び出す -
Push.Sample.Android プロジェクト
主なファイル 概要 AndroidThingsApplication.cs FCM ライブラリの初期化処理を実装 MainActivity.cs FCM ライブラリの初期化処理を実装 BlinkLedService.cs LED 点灯処理が実装されたクラス LogService.cs 簡易ログ出力用のクラス google-services.json FCM を利用するのに必要な設定値が定義されたファイル。Firebase Console からダウンロードする
以下のステップでアプリを作りました。
- 画面表示部分を作る
- FCM を受信する部分を作る
- LED を点灯させる部分を作る
1. 画面表示部分を作る
最初に、Android Things で Xamarin.Forms が動作するかを確認するためにシンプルに画面表示を行う部分を作成しました。
1.1 NuGet パッケージの追加
Xamarin.Android のプロジェクトに Xamarin.Android.Things
の NuGet パッケージを追加します。Android Things そのものが、Developer Preview なので、NuGet も prerelease 版にチェックを入れて、検索を行う必要があります。
1.2 Manifest ファイルの修正
AndroidManifest.xml ファイルに Android Things のライブラリ利用に関する設定を追加します。
<application android:label="Push.Sample.Android">
<!-- その他の設定... -->
<uses-library android:name="com.google.android.things" />
</application>
1.3 デバイス起動時にアプリを起動する設定を追加
Android Things のデバイスを起動したときに、アプリケーションが自動的に起動する設定を MainActivity に追加します。
[IntentFilter(new[] { Intent.ActionMain }, Categories = new[] { Intent.CategoryLauncher })]
[IntentFilter(new[] { Intent.ActionMain }, Categories = new[] { "android.intent.category.IOT_LAUNCHER", "android.intent.category.DEFAULT" })]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
// 中略…
}
1.4 Deploy
adb でデバイスに接続し、Deploy します。
adb connect [IP adress of Android Things device]
adb install [your apk file]
1.5 実行!
Deploy が完了したら、adb reboot
コマンドでデバイスを再起動します。
起動までしばらく待ちます…
アプリの起動成功!画面はあっさりしていますが、これで Android Things 上で Xamarin.Forms が動作することが確認できました。
2. FCM を受信する部分を作る
Xamarin.Forms で FCM のメッセージを受信するために、以下の NuGet パッケージを Push.Sample.Android プロジェクトに追加しました。
なお、このライブラリの使用に際して、以下のバージョンのライブラリを利用しています。
ライブラリ名 | バージョン |
---|---|
Xamarin.Firebase.Messaging | 42.1021.1 |
Xamarin.GooglePlayServices.Basement | 42.1021.1 |
Xamarin.GooglePlayServices.Tasks | 42.1021.1 |
2.1 Firebase プロジェクトの準備
ここに記載されている手順に従って、Firebase のプロジェクトの作成を行い、google-services.json ファイルをプロジェクトに追加します。
2.2 Android Manifest の設定
ここの手順に従って、Android Manifest ファイルの設定、google-service.json ファイルの設定を行います。ネイティブ(Push.Sample.Android)プロジェクトで以下の作業を行いました。
-
<application>
要素に以下の<receiver>
の設定を追加<application android:label="Push.Sample.Android"> <receiver android:name="com.google.firebase.iid.FirebaseInstanceIdInternalReceiver" android:exported="false" /> <receiver android:name="com.google.firebase.iid.FirebaseInstanceIdReceiver" android:exported="true" android:permission="com.google.android.c2dm.permission.SEND"> <intent-filter> <action android:name="com.google.android.c2dm.intent.RECEIVE" /> <action android:name="com.google.android.c2dm.intent.REGISTRATION" /> <category android:name="${applicationId}" /> </intent-filter> </receiver> <!-- その他の設定... --> </application>
-
<application>
要素に 以下のPsermission の設定の追加<uses-permission android:name="android.permission.INTERNET" />
-
google-service.json ファイルのビルドアクションの設定
2.3 FCM の初期化処理
ここの手順に従って、初期化処理を実装します。ネイティブ(Push.Sample.Android)プロジェクトで作業を行いました。
- カスタム Application クラス(AndroidThingsApplication.cs)
[Application]
public class AndroidThingsApplication : Application
{
public AndroidThingsApplication(IntPtr handle, JniHandleOwnership transer) : base(handle, transer)
{
}
public override void OnCreate()
{
base.OnCreate();
#if DEBUG
FirebasePushNotificationManager.Initialize(this, true);
#else
FirebasePushNotificationManager.Initialize(this, false);
#endif
}
}
- MainActivity での初期化処理(MainActivity.cs)
[Activity(Label = "Push.Sample", Icon = "@drawable/icon", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
[IntentFilter(new[] { Intent.ActionMain }, Categories = new[] { Intent.CategoryLauncher })]
[IntentFilter(new[] { Intent.ActionMain }, Categories = new[] { "android.intent.category.IOT_LAUNCHER", "android.intent.category.DEFAULT" })]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
protected override void OnCreate(Bundle bundle)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(bundle);
global::Xamarin.Forms.Forms.Init(this, bundle);
LoadApplication(new App());
FirebasePushNotificationManager.ProcessIntent(Intent);
}
}
2.4 メッセージ受信時の処理
メッセージ受信時の処理は、CrossFirebasePushNotification.Current.OnNotificationReceived
イベントハンドラに実装します。今回は、Xamarin.Forms プロジェクトの MainPage.xaml.cs に実装しました。
public MainPage()
{
InitializeComponent();
CrossFirebasePushNotification.Current.OnNotificationReceived += Current_OnNotificationReceived;
}
private async void Current_OnNotificationReceived(object source, Plugin.FirebasePushNotification.Abstractions.FirebasePushNotificationDataEventArgs e)
{
App.LogService.Info("Push.Sample", "onMessageReceived");
foreach (var data in e.Data)
{
App.LogService.Info("Push.Sample", $"key: {data.Key}, value: {data.Value}");
}
// 後で、LED を点灯させる処理を追加する部分
}
2.4 動作確認
FCM メッセージの送信には、Fiddler を使いました。
-
送信先
-
HTTP ヘッダ
キー 値 Content-Type application/json Authorization key=[FCM プロジェクトのサーバーキー]
サーバーキーは、FCM の[プロジェクトの設定]->[クラウドメッセージング] タブで確認できます。
-
HTTP Body
{ "to": "/topics/command", "data": { "message": "hello world" } }
Device Log を確認すると、FCM から通知を受信していることが確認できました。
3. LED を点灯させるコード
3.1 LED を点灯させるコードの実装
Raspbery Pi で提供されている汎用入出力ポートを利用して、LED を点灯させる処理を実装しました。
汎用入出力(GPIO)ポートと接続を確立するには、Android.Things.Pio.PeripheralManagerService
クラスの OpenGpio
メソッドを利用します。
-
PeripheralManagerService.OpenGpio
- GPIO との接続を確立します
-
OpenGpio
のポート名は、以下のサイトを参照してください - 今回は、"BCM24" のポートを利用しています
汎用入出力ポートとの接続が確立されると Android.Things.Pio.Gpio
のインスタンスが返されます。まずは、ポートに対して書き込み処理を行うという設定を行います。
-
Android.Things.Pio.Gpio.SetDirection
で GPIO から出力を行う設定をする
出力を行う設定を行った後、 Value
プロパティで LED の ON / OFF を切り替えます。
-
Android.Things.Pio.Gpio.Value
プロパティで ON / OFF を設定する
出力が終わったら、接続を閉じます。
-
Android.Things.Pio.Gpio.Close
メソッドで接続を閉じます
以下に BlinkLed
メソッドの引数で秒数を受け取って、その秒数の間 LED を点灯させるサンプルコードを示します。
public class BlinkLedService : IBlinkLedService
{
private const string LED = "BCM24";
private const int INTERVAL_BETWEEN_BLINKS_MS = 500;
private const string TAG = "Push.Sample";
public async Task BlinkLed(int interval)
{
try
{
await Task.Factory.StartNew(() =>
{
Gpio ledGpio = null;
try
{
PeripheralManagerService service = new PeripheralManagerService();
ledGpio = service.OpenGpio(LED);
ledGpio.SetDirection(Gpio.DirectionOutInitiallyLow);
var stopWatch = new Stopwatch();
stopWatch.Start();
while (true)
{
ledGpio.Value = !ledGpio.Value;
SystemClock.Sleep(INTERVAL_BETWEEN_BLINKS_MS);
if (stopWatch.Elapsed.Seconds > interval) break;
}
}
catch (Exception e)
{
Log.Error(TAG, Java.Lang.Exception.FromException(e), "Error on Peripheral I/O API");
}
finally
{
if (ledGpio != null)
{
Log.Info(TAG, "GPIO Closing");
ledGpio.Close();
ledGpio = null;
}
}
});
}
catch (Exception e)
{
Log.Error(TAG, Java.Lang.Exception.FromException(e) ,"Error on Blinking LED");
}
}
}
3.2 FCM メッセージ受信時に LED を点灯する処理を呼び出す
Xamarin.Forms の DependencyService
で LED を点灯するクラスのインスタンスを取得して、FCM メッセージから受け取ったパラメータをBlinkLed
メソッドに渡す処理を MainPage.xaml.cs に追記します。
var intervalKey = "Interval";
int interval = 0;
var service = DependencyService.Get<IBlinkLedService>();
interval = Convert.ToInt32(e.Data[intervalKey]);
await service.BlinkLed(interval);
動かしてみる
Fiddler でメッセージを送信します。
-
HTTP Body
{ "to": "/topics/command", "data": { "Interval": 10 } }
FCM からメッセージを受信して、LED を点灯させることができました。
さいごに
- 意外とあっさり作れてしまって、ハードウェアの敷居を低くしてくれた Raspbery Pi はすばらしいなと感じました。
- Android Things と FCM などの周辺技術を組み合わせることで、いろいろなものが作れそうな気がしてきました。(その前に、電子工作のことをもっと学ぶ必要がありますが...)
- Android Things 上で Xamarin も動作するということで、「Xamarin はいいぞ」と改めて思いました。