.NET Gadgeteer の無線 LAN 接続と時刻同期

※本稿は .NET Micro Framework 4.2 とそれに対応する .NET Gadgeteer SDK を利用したコードを載せています。 .NET Micro Framework 4.3 対応のコードは こちらの記事 を参照してください(残念ながら、4.2 と 4.3 とでは API が違っています)。

 

8月に IoT についてのセッションを 2つ担当しましたが、.NET Gadgeteer のコードの内容はきちんとお伝えしていませんでした。何回かに分けてコードに触れてみようと思います。(やるやるサギっぽいくてすみません)

今回は 無線 LAN 接続 と 時刻合わせ(Network Time Protocol) の紹介をします。セッションでは環境センサーをデモしましたが、まずはネットワーク接続と時刻合わせに話を絞るために、一旦 IoT らしい処理の手前までの短いサンプルを作ってみました。
※動作の確認のために LCD を接続して デジタル時計 を作りましたが、表示の部分は今回の投稿ではオマケです。

WP_20140915_010

メインボードと接続するモジュールは以下の通り。

2014-09-21 21-11-27


無線 LAN 接続 の方法

WiFi RS21 モジュールクラスのインターフェイス (WiFiRS9110) は Scan メソッドを持っています。これで接続したい WiFi のアクセスポイントを探します。
アクセスポイントが見つかったら Join メソッドに WiFi キーを渡して接続完了です。
定型的なコードなので、まずはこの通りの書くんだということでかまいません。

private void SetupWifi()
{
    wifiRS21.Interface.NetworkAddressChanged += Interface_NetworkAddressChanged;

    if (!wifiRS21.Interface.IsOpen)
        wifiRS21.Interface.Open();
    if (!wifiRS21.Interface.NetworkInterface.IsDhcpEnabled)
        wifiRS21.Interface.NetworkInterface.EnableDhcp();

    GHI.Premium.Net.NetworkInterfaceExtension.AssignNetworkingStackTo(wifiRS21.Interface);
    GHI.Premium.Net.WiFiNetworkInfo[] scanResults = wifiRS21.Interface.Scan(Ssid);
    if (scanResults == null)
    {
        PrintMessage("No Networks     ");
        return;
    }

    try
    {
        wifiRS21.Interface.Join(scanResults[0], WifiKey);
    }
    catch (Exception)
    {
        PrintMessage("Can't join to NW");
        return;
    }

    Thread.Sleep(1000);
    PrintMessage("Joined to NW");
}

 


時刻同期の方法

時刻同期も定型的な記述でかまいません。
ただ一つ残念なことがあります。時刻同期で使用する TimeService クラスは FEZ シリーズの中でも Premium ライブラリーにしか含まれていないようです。
よって例えば廉価な Cerberus メインボードなどでは以下のコードは利用できません。

以下の処理を行います。

  1. DNS クラスの GetHostEntry メソッド、さらに AddressList メソッドで NTP サーバーの IP アドレスを取得
  2. TimeService の PrimaryServer プロパティに NTP サーバーの IP アドレスを設定。ただし PrimaryServer は byte 配列型なので GetAddressBytes メソッドが必要
  3. SetTimeZoneOffset メソッドでタイムゾーンの指定 (540 = 9時間 * 60分)
  4. TimeService の Start メソッド呼び出し
  5. 時刻同期に成功するとイベントハンドラーが呼び出される

注意点としては NTP サーバーはネットワーク的に近いところを指定するのがよいようです。例えば “time.windows.com” を指定するとかなりの確率でエラーになります。タイムアウトしてしまうようです。私が試したところでは “ntp.nict.jp” はほぼ確実に時刻が取得できます。

private void SetupLocalClock()
{
    if (wifiRS21.Interface.NetworkInterface.IPAddress == string.Empty)
        return;

    TimeService.SystemTimeChanged += TimeService_SystemTimeChanged;
    TimeService.TimeSyncFailed += TimeService_TimeSyncFailed;

    var ntpAddresses = Dns.GetHostEntry(NtpServer).AddressList;
    if (ntpAddresses == null || ntpAddresses.Length == 0)
    {
        PrintMessage("No NTPServers");
        return;
    }

    Thread.Sleep(1000);
    PrintMessage("Sync Clock Start");

    var settings = new TimeServiceSettings
    {
        PrimaryServer = ntpAddresses[0].GetAddressBytes()
    };

    TimeService.Settings = settings;
    TimeService.SetTimeZoneOffset(540);
    TimeService.Start();

    Thread.Sleep(1000);
    PrintMessage("Sync Clock OK");
}

void TimeService_SystemTimeChanged(object sender, SystemTimeChangedEventArgs e)
{
 TimeService.Stop();
 _watchTimer.Start();

 PrintMessage("Sync Clock");
}

 


デジタル時計の全ソースコードは以下の通り。 WiFi 接続できて時刻が同期できれば、あとはネットワークは不要です。
(という意味では、無線LAN と時刻同期のサンプルとしては少し残念なコードなんですが・・・)

using System;
using System.Net;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Time;
using GT = Gadgeteer;

namespace GadgeteerWatch
{
    public partial class Program
    {
        private const string Ssid = "<ここにSSID>";
        private const string WifiKey = "<ここにKey>";
        private const string NtpServer = "ntp.nict.jp";

        private GT.Timer _watchTimer;

        // This method is run when the mainboard is powered up or reset.
        void ProgramStarted()
        {
            var initTimer = new GT.Timer(5000, GT.Timer.BehaviorType.RunOnce);
            initTimer.Tick += initTimer_Tick;
            initTimer.Start();

            // Use Debug.Print to show messages in Visual Studio's "Output" window during debugging.
            Debug.Print("Program Started");
        }

        void initTimer_Tick(GT.Timer timer)
        {
            characterDisplay.SetBacklight(true);
            characterDisplay.PrintString("Initializing ...");

            _watchTimer = new GT.Timer(1000);
            _watchTimer.Tick += watchTimer_Tick;

            SetupWifi();
            SetupLocalClock();
        }

        void watchTimer_Tick(GT.Timer timer)
        {
            DateTime curDt = DateTime.Now;
            var curTime = curDt.ToString("    HH:mm:ss    ");
            var curDate = curDt.ToString("yyyy/MM/dd (ddd)");

            characterDisplay.SetCursor(0, 0);
            characterDisplay.PrintString(curTime);
            characterDisplay.SetCursor(1, 0);
            characterDisplay.PrintString(curDate);
        }

        private void SetupWifi()
        {
            wifiRS21.Interface.NetworkAddressChanged += Interface_NetworkAddressChanged;

            if (!wifiRS21.Interface.IsOpen)
                wifiRS21.Interface.Open();
            if (!wifiRS21.Interface.NetworkInterface.IsDhcpEnabled)
                wifiRS21.Interface.NetworkInterface.EnableDhcp();

            GHI.Premium.Net.NetworkInterfaceExtension.AssignNetworkingStackTo(wifiRS21.Interface);
            GHI.Premium.Net.WiFiNetworkInfo[] scanResults = wifiRS21.Interface.Scan(Ssid);
            if (scanResults == null)
            {
                PrintMessage("No Networks     ");
                return;
            }

            try
            {
                wifiRS21.Interface.Join(scanResults[0], WifiKey);
            }
            catch (Exception)
            {
                PrintMessage("Can't join to NW");
                return;
            }

            Thread.Sleep(1000);
            PrintMessage("Joined to NW");
        }

        void Interface_NetworkAddressChanged(object sender, EventArgs e)
        {
            PrintMessage(wifiRS21.Interface.NetworkInterface.IPAddress);
        }

        private void SetupLocalClock()
        {
            if (wifiRS21.Interface.NetworkInterface.IPAddress == string.Empty)
                return;

            TimeService.SystemTimeChanged += TimeService_SystemTimeChanged;
            TimeService.TimeSyncFailed += TimeService_TimeSyncFailed;

            var ntpAddresses = Dns.GetHostEntry(NtpServer).AddressList;
            if (ntpAddresses == null || ntpAddresses.Length == 0)
            {
                PrintMessage("No NTPServers");
                return;
            }

            Thread.Sleep(1000);
            PrintMessage("Sync Clock Start");

            var settings = new TimeServiceSettings
            {
                PrimaryServer = ntpAddresses[0].GetAddressBytes()
            };

            TimeService.Settings = settings;
            TimeService.SetTimeZoneOffset(540);
            TimeService.Start();

            Thread.Sleep(1000);
            PrintMessage("Sync Clock OK");
        }

        void TimeService_SystemTimeChanged(object sender, SystemTimeChangedEventArgs e)
        {
            TimeService.Stop();
            _watchTimer.Start();

            PrintMessage("Sync Clock");
        }

        void TimeService_TimeSyncFailed(object sender, TimeSyncFailedEventArgs e)
        {
            TimeService.Stop();
            PrintMessage("Sync Clock Failed");
        }

        private void PrintMessage(string msg)
        {
            characterDisplay.Clear();
            characterDisplay.PrintString(msg);
        }
    }
}

最近、腕時計型のデバイス(スマートウォッチ)が発表されたことは皆さんご存知だと思います。クールですね、かっこいいですね。
そこで作ったのが今回のデバイスです。
製作時間、約2.5時間(そのうち 1.5時間はモジュールの配置を考えるのとねじ止めの時間) の力作です。

WP_20140915_007

非常に個性的な外観で、着用して外出すると間違いなく周囲の注目の的です。
機能は時刻と日付の表示のみに絞り、最近の多くのデバイスが多機能すぎるのとは一線を画しています。

ただし人混みでの利用はケガの元であることと、重量から片腕だけ筋肉質になってしまう可能性があります。普段使いにはくれぐれも注意してください。(笑)

広告
カテゴリー: .NET Micro Framework タグ: , , パーマリンク

.NET Gadgeteer の無線 LAN 接続と時刻同期 への1件のフィードバック

  1. ピンバック: .NET Gadgeteer の無線 LAN 接続と時刻同期 (NETMF 4.3 版) | 技術との戯れ

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中