2024年5月2日木曜日

iOS用のSDKを.NET MAUIで使えるようにしてみる

 RFIDを使ったシステム開発ではスマートフォンでリーダを制御することが多いですが、iOSで使いたいという要望がとても多いです。iOSに対応しているリーダも少ないながらありますのでそのリーダのSDKを使って開発するわけですが、iOSでの開発というものは何と言うか、こう、いろいろ大変ですよね。

ちょこっとしたアプリなら問題無いですが、業務用のアプリになると画面数多いしそれはそれは大変なわけです。そこで少しでも開発を楽にしたいということでXamarinを使うという手があります。

XamarinならC#で書けますので開発は楽です。しかしXamarinのサポートが終わり、.NET MAUIに移行という流れになっています。

そのような事で MAUI で開発していこうと思うのですが、ここで先ほどの SDK が問題になってきます。そう、SDK はネイティブな Framework になっていることが多いのでそのままではMAUIで使用できません。バインディングというものを作成する必要があります。

チュートリアル: iOS Swift ライブラリをバインドする

この資料に沿って作業します。この資料のポイントは、SDKをそのままバインディングするのではなく、必要なメソッドだけラッパー的なライブラリを作ってSDKを後ろに隠し、バインディングはラッパーの部分だけにすることでややこしい作業を省こうという部分です。資料は Xamarin用ですが、まぁ、何とかなるかなと思ってやってみます。

ちなみに MAUI だと Mac がなくても iOS の開発ができるみたいな感じですが、さすがにバインディングのプロジェクトはビルドできなかったのでライブラリを作るところまでは Mac で作業します。

早速問題があって手元にある Mac mini が古くて OS が Catalina までしか上げられないので必然的に Xcode が Version 12.4 までしか入りません。

上記資料に沿って作業を行い、何とか「ネイティブライブラリをビルドする」の章まで完了しました。

次の章「メタデータを準備する」でコケます。Objective Sharpieというツールを使って、バインディングライブラリに必要な ApiDefinition.cs と StructsAndEnums.cs を作成する必要があるんですが、OSが古いせいか sharpie がエラーで起動すらしてくれません。困った。どうしよう。ただどちらのファイルもライブラリの定義情報を記載してるだけのようなので手作業で作ることにします。手作業と言っても、作成するのはラッパーの部分だけなので楽勝です。

次、「バインドライブラリをビルドする」の章、資料では Visual Studio for Mac で作業してます。Visual Studio 使うんなら Windows でもよくね?と思って試しに Windows の Visual Studio 2022 でやってみます。

ネイティブ参照などはうまくいって何かうまくいきそうな気配がする。画面見る限り何の文句も言ってこないしこれはこのまま行けるのか!? が、ビルドでエラー、ApiDefinition.cs に記載している NSObject を知らないと言う。え?何で?基本型じゃん。

エラーをよくよく見るとMacが無いとビルドできないというメッセージも出ている。ダメか。結局Macか。ということで大人しく Visual Studio for Mac で作業することにする。が、Visual Studio for Mac をインストールしているとおまえの Xcode が古いから使えない機能があるかも的なメッセージが出る。無視して進む。

ビルドまでできるようになったが .NET 7.0 でビルドされている様子。ターゲットを.NET 8.0にしようとするが、インストールされていないと出てくる。別途 .NET 8.0 SDK をインストールするが、相変わらず.NET 8.0はインスールされていないと出る。仕方ないので .NET 7.0 のものをそのまま使うことにする。

ビルドしてできた DLL 等を Windows に持ってくる。これが使えるのかが問題だ。まず MAUI のプロジェクトを作り、例のDLLを参照してみる。参照できた!ビルドも通った。あとは実機で動くかどうかだ。早速 iPhone を繋いで実行しようとすると iTunes が必要と言われるのでインストールする。

すると実機で動いた!上記以外に他にも細かいトラブルはたくさんありましたが、何とか動作するところまでたどり着きました。あー長かった。

2024年4月26日金曜日

AWSで管理しているLoRA WANデバイスの情報を取得する AWS IoT Wireless

  AWSのIoT Coreでは、LoRA WAN通信のデバイスを登録してデバイスの状態をクラウドから取得することができます。デバイスの登録はコンソール画面から行うのですが、いろいろと面倒なところがあり、これをAPIっぽくカスタムのサイト上で行えるようにしたくなりました。ところが参考となる資料がなかなかないため、ここで紹介します。

必要なパッケージ

using Amazon.IoTWireless.Model;
using Amazon.IoTWireless;
using Amazon.Runtime;

登録しているデバイスの取得

var credentials = new BasicAWSCredentials("YOUR_KEY", "SECRET_KEY");

AmazonIoTWirelessClient client = new AmazonIoTWirelessClient(credentials, Amazon.RegionEndpoint.APNortheast1);

ListWirelessDevicesRequest request = new ListWirelessDevicesRequest(); request.WirelessDeviceType = WirelessDeviceType.LoRaWAN; request.MaxResults = 100;

ListWirelessDevicesResponse response = client.ListWirelessDevices(request);

デバイスの登録

var credentials = new BasicAWSCredentials("YOUR_KEY", "SECRET_KEY");

AmazonIoTWirelessClient client = new AmazonIoTWirelessClient(credentials, Amazon.RegionEndpoint.APNortheast1);

OtaaV1_0_x otaa = new OtaaV1_0_x() {

    AppKey = appkey,     AppEui = appeui };

LoRaWANDevice lora = new LoRaWANDevice {     OtaaV1_0_x = otaa,     DevEui = deveui,     DeviceProfileId = deviceprofileid,     ServiceProfileId = serviceprofileid };

CreateWirelessDeviceRequest request = new CreateWirelessDeviceRequest {     Name = devname,     LoRaWAN = lora,     DestinationName = destination,     Type = WirelessDeviceType.LoRaWAN };

名前はオプションですが、これで最低限のデバイスの登録が行えます。ほかにもデバイスの説明や位置情報なども追加できます。

2024年4月24日水曜日

LoRaWAN のローミングを試す

 LoRaWANはゲートウェイを中心としたスター型の通信になっていますので、基本的には1台のゲートウェイと通信をするんですが、ローミングの機能もあってゲートウェイが変わっても継続して通信することができます。LoRaWANでは通信開始時にJOINという手続きが必要なんですが、ここでの継続して通信というのはこのJOINをゲートウェイが変わってもやり直さなくてよいということになります。

今回はこれを実際に試してみようと思います。ちなみにLoRaWANサーバはAWSのIoT Coreを使用しています。

まず事務所に設置してる1台のゲートウェイから離れた場所に移動して、一時的にゲートウェイを動かしてそちらでJOINを行います。


JOINできたのを確認して、一応データ送信してみます。

うまく飛んでます。ここでゲートウェイは電源を落として、PCは電源落とさないように注意して事務所まで移動します。


窓にゲートウェイ貼っつけてあります。

ここで先程のPCで続けてデータ送信してみます。

あっさり飛びました。すぐに応答があったので、JOIN等の手続きをしているようには見えません。これで一度JOINすれば、通信するゲートウェイが変わっても問題なく通信できることがわかりました。




2024年2月28日水曜日

頭に近づけるとリーダーの読み取り距離は伸びるのか実験

車のロックを遠隔で解除する、鍵についているリモコンキーがありますね。以前このリモコンキーについて面白い記事を見かけました。
どうやら鍵を頭に近づけてボタンを押すと、車のロックをより遠くの距離から解除できるそうです。前から裏技としてこのテクニックは知られていたようで、シミュレーションで検証する記事もありました。

さらに気になるのはこのリモコンキーが扱う周波数です。
二つの記事をざっと見た感じだとだいたい300MHzあたりのようで、超短波(VHF)寄りの極超短波(UHF)になるのでしょうか?

もしかしたらこのリモコンキーのテクニックはRFIDリーダーにも応用できるかもしれません。
そういうわけで、実際にUHF帯のリーダーを頭やペットボトルに近づけたりして距離が変わるのか試してみました。

広い空間で検証する必要があったので、今回はDENSOさんのハンディリーダーSP1を使用します。
SP1の周波数はだいたい916~920MHzになります。リモコンキーの周波数とは結構差があると思いますが、試してみます。



屋外なのでカバンのなかにタグをいれ、これを読み取れる距離をはかります。


結構簡単な検証だと思っていましたが、屋外だと風があるせいか、標準になる限界の距離がどこなのかなかなかつかめませんでした。

これでだいたい5mくらいでした。
ここからリモコンキーの記事にあったように、リーダーを頭やあごあたりに近づけつつ後ずさりしてみました。

頭に近づけた結果としては、確かに距離が伸びたように感じる瞬間があった、程度でした。
リーダーを頭に近づけたり離したりを繰り返すと、読み取りもそれに応じて、頭に近づけるたびに反応があるということが起きました。しかし結構偶然に近いような感じで、あまり再現できませんでした。

今度は天然水のペットボトルを使います。


頭に近づける必要があるのは、頭にアンテナのように電波を延長させてくれる水分があるためなので、これをリーダーに近づければもっと効果があるかもしれません。


結果としては、頭に近づけるときと同様にイマイチでした。


やはりICタグと水分の相性は悪いと考えたほうがよいのでしょうか。
もっと電波について勉強したいと思える実験でした。

2024年2月27日火曜日

LoRaWANの電波強度を測定してみる

 おかげさまで最近よく沖縄のニュースなどで取り上げていただいている「ミマモライド」ですが、こちら通信に LoRaWAN を使用しております。エンドデバイスは自動販売機などに設置していて地面に近いところにあります。なのでゲートウェイはなるべく高いところに設置をしています。

使用しているゲートウェイは DRAGINO の DLOS8 です。

ところが、設置後に通信のテストしていると、あれ?何か届かなくね?みたいのが発生したりします。よくよく調べると、アンテナのコネクタがきちんと刺さってなかったのか、そのあたりをイジってるときちんと電波飛ぶようになるときがあります。

設置場所は気軽に入れない場所がほとんどで、後から何か電波弱いね―みたいになると非常に面倒です。なのでアンテナが付いているときと付いてないときで電波強度にどのくらいの差が出るのかを事前に測定したいと思いました。

アンテナをきちんと接続した状態と、アンテナを外した状態でそれぞれ10m、20m、30m離れたところから2回ずつ電波を出して、ゲートウェイでどのくらいのRSSIで受信できたのかを測定しました。


結果がこちら。SFはすべて10でした。いちおう各測定ごとにJOINから開始しています。これを見るとやはり結構な差が出ています。これをもとにゲートウェイ設置後、近くで送信してみてRSSIが-50くらいであれば大丈夫というような認識でOKのようです。

2023年10月2日月曜日

GAE/J で排他制御したい

 GAEとっても便利なんですが、どうしても排他制御したい処理が出てきてしまいました。特定のファイルの更新とかそんな感じの処理です。syncronized で同じインスタンスなら排他制御できるので大丈夫なんですが、GAEの場合オートスケールするので同じインスタンスからのアクセスとは限らずその技は通用しません。

ググるとこういうときは Memcache を使って実現するようなのが多いようです。

Memcache の使用方法

これの、「同時書き込みの処理」のところを使って、ロックフラグみたいのをいじるようにすると実現できそうです。

というわけで実装してみたわけですが、、、ちゃんと動作しません。なんだろう、ソースそのまま使ってるしなーと思って上記のソースを眺めていると、、、あれ?これってキーがまだ無いときに複数同時に処理されると皆んな0で作っちゃうんじゃね?ということに気づきました。

ここの処理の肝は putIfUntouched を使って、特定の1スレッドだけが処理を完了できるところにあります。が、キーが無いときはただ 0 を put してるだけなのでここが被ると排他にならないわけです。

putIfUntouched で 0→1 は特定の1スレッド限定にできるんですが、 null→0 をやりたいときは putIfUntouched は IllegalArgumentException を投げてくるので使えません。

キーがない → 0でput → putIfUntouched で1にできた人勝ち

とやりたいところなんですが、勝ちが決まった瞬間に0でputの人が出てくると上書きされてしまうのです。

こまった。どうしたらいいんだ。ということで思いついた苦肉の策が、「putした人はしばらくロックバトルに参加できなくする」という作戦です。putした人の動きをしばらく止めておけば、後から来た人はキーが存在する状態なので putIfUntouched で勝者を決めることができます。

    public void LockFile(String filePath) {
        Random rand = new Random();
        MemcacheService syncCache = MemcacheServiceFactory.getMemcacheService();
        
        while (true) {
            byte[] oneValue = BigInteger.valueOf(1).toByteArray();
            byte[] zeroValue = BigInteger.valueOf(0).toByteArray();

            IdentifiableValue lockValue = syncCache.getIdentifiable(filePath);
            if (lockValue == null) {
                syncCache.put(filePath, zeroValue, Expiration.byDeltaSeconds(30));
                try {
                    Thread.sleep(1000);  // ロック情報を作成したスレッドは1秒間ロック取得競争に参加できないようにする
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } else {
                if (new BigInteger((byte[])lockValue.getValue()).intValue() == 0 && syncCache.putIfUntouched(filePath, lockValue, oneValue, Expiration.byDeltaSeconds(30))) {
                    // ここに来れるのは1スレッドのみ
                    logger.info("Lock File : " + filePath);
                    break;
                }                
            }
            
            try {
                Thread.sleep(rand.nextInt(100));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }        
    }
    
    public void UnlockFile(String filePath) throws Exception {
        MemcacheService syncCache = MemcacheServiceFactory.getMemcacheService();
        syncCache.put(filePath, BigInteger.valueOf(0).toByteArray(), Expiration.byDeltaSeconds(30));        
        logger.info("Unlock File : " + filePath);
    }

いちおうこれで考えてたような処理が無事できました。

競合が居ない(かつキーが無い)場合は最低1秒待たされることになりますがまぁ仕方ないかな。

2023年7月19日水曜日

AGCさんの電波吸収遮へいガラスを試してみた

 従来の電波吸収体といえばパネルやカーテンみたいなもので遮蔽すると向こうが見えないのが普通でした。これをシースルーで実現させたのが、先日のリテールテックでも展示してありましたAGCさんの電波吸収遮へいガラスです。

東芝テックのRFIDウォークスルーゲート、AGCの電磁波吸収/遮蔽ガラスを採用

この製品のサンプルをAGCさんのご厚意によりお借りすることができましたので実際に試してみました。

試すにあたっていろいろ設置してやってみたのですが、電波の反射や回り込みの影響があるのでなかなか効果を測定することが難しかったので最終的にこんな感じで測定をすることにしました。

簡易測定装置

床にアンテナを置いて、そのすぐ上に電波吸収ガラスを設置します。これだと反射や回り込みもあまり影響しないのでうまく測定できるかなと。上に付いてるのは簡易的なスペアナのアンテナです。

実際にガラスを置いたところ

リーダはマスプロさんの1W機を使いました。アンテナが大きいのが無かったのでシートアンテナを使いました。このアンテナは利得が3dBiなのでフルパワーとはいきませんがまぁ、これしか無かったのですいません。

電波を出してみて、スペアナで見てみます。

ガラス無しの状態

ガラスありの状態

明らかに山の形が小さくなっているのがわかります。ピークっぽいところを比較すると、ガラス無しで 20dB がガラスありで 35dB くらいになってるので電力で換算すると30分の1くらいになってます。これはスゴいですね!

実際にタグを置いて読ませると、


4枚置いてますが2枚しか読めませんでした。読めた2枚もRSSIで見ると-80くらいだったのでかろうじて読めてるという印象です。シースルーでここまで出来るとは驚きでした。