Garminウォッチ用のデータフィールドを作成する(その2)

前回は、ガーミンウォッチ用のデータフィールドの作成をVisual Studio Codeを使ってプログラムするところまでやりました。
alasixosaka.hatenablog.com
今回は、出来たデータフィールドをウォッチに転送して使えるようにします。
まず、ウォッチフェイスの時と同様に、プロジェクトのフォルダを開いて、binフォルダ内にあるxxxx.prgというファイルを探します(xxxxはプロジェクト名)。
ウォッチをPCに接続して、Appフォルダに先ほどのファイルをコピーします。
PCでの操作はここまで。ウォッチをPCから外して、あとはウォッチ側での操作です。
ウォッチフェイスの場合はここでウォッチフェイスを選択すれば表示されたのですが、データフィールドの場合はもうひと手間必要です。
ウォッチで、Startボタンを押して、データフィールドを追加したいアクティビティを選択します。自分の場合はトレイルランニングに追加したいので、トレイルランニングを選択。Upボタンを長押ししてトレイルラン設定を選択、トレーニングページを選択、そうするとデータフィールドが現れるので、Upボタンを押して追加のページ(画面の中央に緑の+が表示される)を表示させてStartボタンで選択。カスタムデータを選択し、レイアウト選択画面で1項目を選択する。ここで、2項目以上を選択してしまうと、前回シミュレータで表示させたように全項目同じ表示になってしまうので注意。
ここで、一番上に「ConnectIQ 1/xを追加しました」というような表示あるので、これを選択。そうすると、リストにさきほどコピーしたxxxx(プロジェクト名)が表示されるのでこれを選択するとそのアクティビティに自作のデータフィールドが追加される。別のアクティビティ(例えばラン)に追加する場合は同じ操作をランのアクティビティ設定で行う。
追加したら、データフィールドをどの順番に配置するか選択する画面が出てくるので好きなところに配置する。デフォルトだと一番最後に配置される。

別のデータフィールドを作ってみる

もう少し別のデータフィールドも作ってみます。
実は、やりたいことがあったのですが、どうもうまくいかないようです。それは、

distanceToNextPoint、distanceToDestinationは使えない

ということのようです。上の2つのコマンドはAPIのコマンドリストに掲載されていて、ナビゲーション中に次のポイントや目的地(ゴール)までの距離を取得する関数のようです。ところが、FenixやForerunner955などの地図を表示できるデバイスは対応デバイスになっているのですが、Forerunner255は対応デバイスのリストに載っていません。ただ、Instinct2などの地図を表示できないデバイスもリストにあるのでひょっとしたら使えるのではないかと思って試してみました。
結論から言うとForerunner255では使えないようです。Instinct2で使えて、Forerunner255で使えない理由は不明ですが、あると便利な機能なのでちょっとショックでした。一応ナビゲーション中に地図の画面(といっても地形や道路は表示されないですが)からDownボタンでその下の画面に行くとGarminConnectで設定したポイントで次のポイントまでの距離やゴールまでの距離が表示されます。地図画面でもゴールまでの距離は表示されます。
ただ、残り距離を現在時刻やトータルタイムと一緒に表示できればいいなと思ったのですが、どうもForerunner255では無理なようです。
デフォルトの設定でも表示される情報なので、時計の機能自体に対応できないという問題があるわけではなさそうですが、どうもConnectIQのAPIが対応していないようです。
APIにはnameOfNextPointというコマンドもあるので、nameOfNextPointとdistanceToNextPointを表示させるデータフィールドを作ってみました。ナビゲーションを実行しないと機能しない(コースとコース上のポイントを読み込まないと距離の計算ができないはず)なので、実機で、自宅の周りのコースを適当にGarminConnectで作ってウォッチに転送して動かしてみましたが、表示はnameもdistanceもNullとなってデータが表示されませんでした。
ちなみに、ConnectIQストアで次のポイントまでの距離や名前を表示するデータフィールドがあり、対応デバイスにForerunner255が載っていたので、これも試してみましたが、なんか全然変な距離と名前が表示されるだけで全く機能しませんでした。ということで、この機能を実装することは諦めました。

ペースを表示させる

仕方がないので、ペースを表示させるデータフィールドを作ってみました。
表示させるデータは、平均のペースと現在のペース、それに距離と経過時間の4つとしました。データフィールドの区切りは、一番上が距離、真ん中を2つに区切って左が平均ペース、右が現在のペース、一番下が経過時間としました。

ペースを表示させるデータフィールド

全部白黒なのも味気ないので色を付けてみました。
ペースを直接取得する関数はないので、現在のスピードと平均スピードから計算します。現在のスピードはinfo.currentSpeed、平均スピードはinfo.averageSpeedで取得することができます。どちらも単位はm/sとなっています。ですので、この値からペース(分/km)を計算するには、次の式を実行すればよいことになります。
ペース=1000/60/スピード

プログラム

プログラムのソースです。前回のデータフィールドと基本は大きく変わらないので変わったところだけ説明します。

変数
    hidden var mValue as Numeric;
    hidden var valueFormat = "%d";	
    hidden var label = "距離";  // intial value for the label
    hidden var label2 = "平均ペース";
    hidden var label3 = "タイム";
    hidden var label4 = "ペース";
    hidden var clockTime;
    hidden var eTime;
    hidden const ZERO_TIME = "0:00";
    hidden const ZERO_TIME2 = "00";
    hidden var distanceUnits = System.UNIT_METRIC;
    hidden var distance;
    hidden const ZERO_DISTANCE = "0.0";
    hidden var kmOrMileInMeters = 1000;
    hidden var ave;
    hidden var pace;

変数です。label1からlabel4まではデータフィールドの表示するラベルでそれぞれ、距離、平均ペース、タイム、ペースとなっています。あとは、最後の2つaveとpaceを追加しています。それぞれ平均速度、現在の速度を格納する変数です。

onCompute

    function compute(info as Activity.Info) as Void {
        // See Activity.Info in the documentation for available information.
        //clockTime = System.getClockTime();
        //eTime = info.elapsedTime;
        eTime = info.timerTime;
        distance = info.elapsedDistance;
        ave = info.averageSpeed;
        pace = info.currentSpeed;
    }

onComputeも最後の2行が変わっています。aveに平均速度、paceに現在の速度を格納しています。

onUpdate
    function onUpdate(dc as Dc) as Void {
        var width = dc.getWidth();
        var height = dc.getHeight();
        var textCenter = Graphics.TEXT_JUSTIFY_CENTER | Graphics.TEXT_JUSTIFY_VCENTER;
        var backgroundColor = getBackgroundColor();
        // set background color
        dc.setColor(backgroundColor, Graphics.COLOR_TRANSPARENT);
        dc.fillRectangle (0, 0, width, height);
        // set foreground color
        dc.setColor(Graphics.COLOR_DK_BLUE, Graphics.COLOR_TRANSPARENT);
        dc.drawLine(0,85,width,85);
        dc.drawLine(0,165,width,165);
        dc.drawLine(width/2, 85, width/2, 165);
        
        dc.setColor((backgroundColor == Graphics.COLOR_BLACK) ? Graphics.COLOR_WHITE : Graphics.COLOR_BLACK, Graphics.COLOR_TRANSPARENT);
        
        
        
        //var timeString = Lang.format("$1$:$2$:$3$", [clockTime.hour, clockTime.min.format("%02d"), clockTime.sec.format("%02d")]);
        //var timeString2 = Lang.format("$1$:$2$:$3$", [eTime.hour, eTime.min.format("%02d"), eTime.sec.format("%02d")]);
        var duration;
        var duration2;
        // do layout
        if (isSingleFieldLayout()) {
            dc.drawText(width/2, 30, Graphics.FONT_TINY, label, textCenter);
            dc.drawText(width/2, 180, Graphics.FONT_TINY, label3, textCenter);
            //dc.drawText(width/2, 210, Graphics.FONT_NUMBER_MILD, timeString, textCenter);
            dc.drawText(65, 100, Graphics.FONT_TINY, label2, textCenter);
            dc.drawText(190, 100, Graphics.FONT_TINY, label4, textCenter);
            dc.drawText(190, 70, Graphics.FONT_TINY, distanceUnits == System.UNIT_METRIC ? "km" : "mi", textCenter);
            var distStr;
            var aveStr;
            var paceStr;
            System.println(distance);
            if (distance == null) {
                distStr = ZERO_DISTANCE;
                }
            else {
                var distanceKmOrMiles = distance / kmOrMileInMeters;
                distStr = distanceKmOrMiles.format("%.1f");
            }
            dc.drawText(width/2, 60, Graphics.FONT_NUMBER_MEDIUM, distStr, textCenter);
            if (eTime != null && eTime > 0) {
                var hours = null;
                var minutes = eTime / 1000 / 60;
                var seconds = eTime / 1000 % 60;
                
                if (minutes >= 60) {
                    hours = minutes / 60;
                    minutes = minutes % 60;
                }
            
                if (hours == null) {
                    duration = "0:"+ minutes.format("%02d");
                } else {
                    duration = hours.format("%d") + ":" + minutes.format("%02d");
                }
                duration2 = seconds.format("%02d");
            } else {
                duration = ZERO_TIME;
                duration2 = ZERO_TIME2;
            } 
            dc.drawText(110, 210, Graphics.FONT_NUMBER_HOT, duration, textCenter);
            dc.drawText(190, 215, Graphics.FONT_NUMBER_MILD, duration2, textCenter);
            if (ave==null){
                aveStr = ZERO_DISTANCE;
            }else{
                //var aveSpeed = ave*60/1000;
                var aveSpeed = 1000/60/ave;
                aveStr = aveSpeed.format("%.1f");
            }
            if (pace==null){
                paceStr = ZERO_DISTANCE;
            }else{
                //var paceperkm = pace*60/1000;
                var paceperkm = 1000/60/pace;
                paceStr = paceperkm.format("%.1f");
            }
            dc.setColor(Graphics.COLOR_DK_RED, Graphics.COLOR_TRANSPARENT);
            dc.drawText(60, 130, Graphics.FONT_NUMBER_MEDIUM, aveStr, textCenter);
            dc.setColor(Graphics.COLOR_DK_BLUE, Graphics.COLOR_TRANSPARENT);
            dc.drawText(190, 130, Graphics.FONT_NUMBER_MEDIUM, paceStr, textCenter);
        } else {
            dc.drawText(width / 2, 5 + (height - 55) / 2, Graphics.FONT_TINY, label, textCenter);
            dc.drawText(width / 2, (height - 23) - (height - 55) / 2 - 1, Graphics.FONT_NUMBER_HOT, mValue.format(valueFormat), textCenter);
        }

まず、画面のレイアウトですが、画面を4分割するのでそのための線を3本引いています。最初の2本は前回と同じで、3本目が真ん中を2つに分割する線です。
dc.drawLine(width/2, 85, width/2, 165);
それから、平均ペース、現在のペースを格納する文字列変数を定義しています。
var aveStr;
var paceStr;
経過時間の表示が前回は真ん中だったのを今回は一番下にしたので、表示位置を変更しています。
dc.drawText(110, 210, Graphics.FONT_NUMBER_HOT, duration, textCenter);
dc.drawText(190, 215, Graphics.FONT_NUMBER_MILD, duration2, textCenter);
平均ペースと現在のペースの計算と表示ですが

            if (ave==null){
                aveStr = ZERO_DISTANCE;
            }else{
                //var aveSpeed = ave*60/1000;
                var aveSpeed = 1000/60/ave;
                aveStr = aveSpeed.format("%.1f");
            }
            if (pace==null){
                paceStr = ZERO_DISTANCE;
            }else{
                //var paceperkm = pace*60/1000;
                var paceperkm = 1000/60/pace;
                paceStr = paceperkm.format("%.1f");
            }
            dc.setColor(Graphics.COLOR_DK_RED, Graphics.COLOR_TRANSPARENT);
            dc.drawText(60, 130, Graphics.FONT_NUMBER_MEDIUM, aveStr, textCenter);
            dc.setColor(Graphics.COLOR_DK_BLUE, Graphics.COLOR_TRANSPARENT);
            dc.drawText(190, 130, Graphics.FONT_NUMBER_MEDIUM, paceStr, textCenter);

の部分がそうです。距離の表示と同じように、データがない時はそのままだとNullになってしまうので、データがnullの時をif文で判断して、それ以外の場合は計算して結果を文字列に代入して表示しています。処理の流れは距離の所とほぼ同じです。表示する前にカラーを指定して、平均ペースはダークレッド、ペースはダークブルーにしています。
経過時間の表示がデフォルトでも気にならないのなら、このデータフィールドをわざわざ作る意味はあまりありません。前回も書いたように、デフォルトでの経過時間の表示は何故か、分と秒が大きな文字で、時がとても小さな表示になります。ウォッチ自体の設定でこれを変える方法がないので、オリジナルデータフィールドの価値がそこにあります。
また、マラソン大会専用のデータフィールドを作ったりしている人もいるようなので、データフィールドについてはもう少し研究してみたいと思います。

Garminウォッチ用のデータフィールドを作成する

新しく買ったGarmin Forerunner255用に自作のウォッチフェイスを作成しました。いろいろ苦労したのですが、何とか満足のいくウォッチフェイスができました。
alasixosaka.hatenablog.com
ウォッチフェイスの作成に使ったConnectIQはウォッチフェイス以外にもウィジット、アクティビティ、データフィールドの作成などオリジナルのアイテムを色々と作ることができます。
今回は、データフィールドの作成にトライしてみました。
データフィールドを作成しようと思ったのは、アマゾンのレビューにも出ていますが、アクティビティ中の経過時間の表示が、時、分、秒で表示されるのですが、何故か時の表示だけとても小さい文字になっているのです。
www.amazon.co.jp
上のリンクの写真はForerunner955のものですが、Forerunner255でも同様でした。
そこで、自分でデータフィールドを作ればこの問題は解決するのではないかと考えました。
あと、Forerunner955では表示されるみたいですが、Forerunner255ではナビゲーション中の高低図が表示されません。(Forerunner255で高低図が表示されることがわかりました)これもオリジナルのデータフィールドを作ればなんとかなるのではと思っていますが、こちらの方ははるかに難易度が高そうです。ですので、まずは、普通のデータフィールドを作成し、時間の表示を何とかしたいと思います。

はじめは結構戸惑った。

前回の最後に書いたように、ウォッチフェイスの作成については日本語の参考サイトがいくつかったのでその助けを借りてなんとか完成させることができました。
ところが、データフィールドについてはほとんど日本語の”先生”がいないのです。仕方がないので、英語のサイトやAPI を見ながら進めていきました。
結論から言うと、ウォッチフェイスができれば、あまり問題なくデータフィールドも作成することができます(複雑なことをしなければ)。
一番参考になったのはこちらのサイトです。
starttorun.info
データフィールド作成のためのチュートリアルで、ベースはこのチュートリアルのLayout with dc.draw() callsを参考にしました。ウォッチフェイスの時にも書きましたが、文字列、画像などを配置するときにlayout.xmlを使うのか、直接drawコマンドを使うのか2通りの方法があります。layout.xmlを使う方がフレキシビリティーが高く、いろいろなウォッチに対応させやすいという反面、手続きが面倒というデメリットがあります。自分専用に作る場合は手間を考えるとdrawコマンドを使った方が簡単にできます。ただ、デフォルトのひな形はlayoutを使うようになっていたので、始めは、上記のサイトの存在を知らなくて、ひな形を使っていろいろ試していて、うまくいかず、随分時間を使ってしまいました。

出だしの部分でつまずく

上にも書いたようにまず、ひな形を使ってデータフィールドのプロジェクトを作ってみたのですが、まずはデータフィールド用のプロジェクトを作るところでつまずいてしまいました。データフィールドの作成は、Visual Stuido CodeのMonkey C:New projectで作成するのですが、その時、data fieldを選ぶと、simple と complexの2つの選択肢が出てきます。最初、simpleでは表示するデータが1つだけのデータフィールドを作成でき、complexでは2つ以上のデータを表示するデータフィールドを作成できるものと思っていました。また、シミュレーターで、データフィールドを動かすと、data fieldsのタグからlayoutを選択すると表示するデータ項目数を変更することができて、1から6まで選ぶことができます。このこともあり、complexでは2つ以上のデータを表示するためのものと思ってしまいました。なので、始めは、complexを選択し、出来たlayout.xmlを見て、layout idが5つもあるので、これはデータを5個表示する用のひな形だと勝手に思い込んでいました。ひな形を動かすと、心拍数を表示するデータフィールドができているのですが、このとき、シミュレータで先ほどのdata fieldsタブから4のレイアウトを選ぶとこんな感じに全部同じ表示になってしまいます。

4つとも同じ表示に

当然、5つでも同じことで、全部表示が同じです。
これは、layout.xmlでlayout id が全部同じラベルを持っているからそうなるのだと思って、それぞれ違うラベルに変えてみたものの結果は同じで、ここで、ああでもないこうでもないと試行錯誤やWEBサイトの検索を繰り返していました。
そのうちに、データフィールドのMonkey Cプログラムを公開しているサイトを見て、どうしているのかを調べてみると、そんな複雑なことはせずに、自分で勝手に区切りを入れて、その区切りに合うようにテキストを配置しているだけだと気づきました。
たしかに、シミュレータでもウォッチの実機でも、データフィールドを最初から区切っていくことができるのですが、よくよく考えると、そうすることのメリットは、ユーザー側でどこに、どのデータを割り付けるのかを指定できることくらいしかなく、自分で設計して、決めたデータを決めた場所に配置するだけなら、自分で勝手に区切ってその区切り応じて配置した方がはるかに簡単だということにようやく気付きました。まあ、最初から4つなら4つに区切ったデータフィールドに対応してプログラムを書く方法もあるのかもしれませんが、今のところそのやり方を見つけられていません。でも、自分で勝手に区切ればよいのであれば何も難しいことはなく、ウォッチフェイスでやったように、どこに何を配置するかを決めて、フォントサイズなんかを決めてやればいいだけです。区切りの線が欲しければ自分でこれも書いてやればいいだけの話。

プロジェクトの作り方

まあ、ここまでがいつもの前置き(失敗談)で、ここからが本番です。
先のリンクのチュートリアル layout with dc.draw() callsに従ってプロジェクトを作ります。
作り方は、Visual Studio Code でF1、Monkey C: New Project からデータフィールドを選択します。ここで、上にも書いたようにsimple と Complexの選択肢が出てきますが、complexを選びます。データフィールドの名前を適当に入れるとひな形ができます。このとき、上にも書いたようにlayout.xmlを使うひな形できるので、チュートリアルに従い余分なファイルを削除します。

  • まず、layout.xmlを含むフォルダを削除します。これは、Visual Studio Code上で行います。以下も同じです。
  • 次にxxxxBackground.mcを削除します。xxxxはプロジェクト名です。
  • 次に、xxxxView.mcの中の、function onLayout() を削除します。
  • 次に、同じくxxxxView.mcの中のfunction onUpdate()の中身を空にします。

これで準備完了です。あとは自分の好きなようにレイアウトしてデータを配置するだけです。

まず、データ3つのデータフィールドを作ってみる。

トレラン中に絶対に見る情報として、自分の場合は、トータル距離、経過時間、現在時刻の3つです。老眼であることも考えてとりあえず3つのデータに絞って大きめのフォントで見やすいデータフィールドを目指します。もちろん、この3つのデータを並べて表示するだけなら、ウォッチ上で操作して、新規のデータフィールドを作成し、それぞれにトータル距離、経過時間、現在時刻を割り当てれば済む話で、なにもわざわざオリジナルのデータフィールドを作る必要はないはずですが、上の方にも書いたように、そうすると経過時間の時、分、秒の時のフォントがなぜかとっても小さなフォントで表示されてしまい、老眼で読むのはとてもつらいことになってしまいます。もちろん、1時間なのか、2時間なのかは見なくてもわかることが大半だし、現在時刻を見て逆算すれば仮にフォントが小さくて読めなくても見当はつくのですが、疲れてきて、頭が白くなってきた状態でいろいろ考えるのは面倒なので、大きく表示できるのなら表示したい。ところが、ウォッチを操作して作るデータフィールドはフォントの指定ができないという欠点があり、字を大きくすることができないのです。なので、自分でフォントサイズを指定できるオリジナルのデータフィールドの出番です。

配置を決める

配置は、上から順に、トータル距離、経過時間、現在時刻としました。そして、一応仕切りの線を入れてみました。線の色は、ウォッチを操作して作るデータフィールドと区別するために青のラインにしてみました。フォントのサイズは、一番大きい真ん中の経過時間の時と分がFONT_NUMBER_HOT。秒はそれほど重要ではないので少し小さめのFONT_NUMBER_MILDとしました。トータル距離はFONT_NUMBER_MEDIUM。時刻は表示桁数が多いので小さめのFONT_NUMBER_MILDとしています。また、それぞれのラベルは、FONT_TINYを使いました。

今回作ったデータフィールド

プログラム

プログラムの本体xxxxView.mc (xxxxはプロジェクト名)を順番に説明していきます。drawコマンドを使っていますので、本体以外はmanifest.xmlとxxxxApp.mcの2つのファイルだけで、どちらもデフォルトから手を加えていませんので説明は省略します。

モジュールの読み込み

冒頭の部分です。

import Toybox.Activity;
import Toybox.Graphics;
import Toybox.Lang;
import Toybox.WatchUi;

4つのモジュールを読み込んでいますが、この部分はデフォルトのままです。いまだにimportとusingの区別がわかっていません。ネットでよく見るソースコードではたいていusingを使っています。どちらも大差ないようですがとりあえずデフォルトのままにしてあります。

変数

モジュールの読み込みの次からプログラムの本体が始まります。プロジェクト名はdatafield2としていますので、ファイル名がdatafield2View.mcでクラス名定義でdatafield2View extends WatchiUi.DataFieldとデータフィールド関連の関数を呼び出せるように宣言しています。その次がプログラム全体で使う変数群です。

class datafield2View extends WatchUi.DataField {
    hidden var mValue as Numeric;
    hidden var valueFormat = "%d";	
    hidden var label = "距離";  // intial value for the label
    hidden var label2 = "時刻";
    hidden var label3 = "タイム";
    hidden var clockTime;
    hidden var eTime;
    hidden const ZERO_TIME = "0:00";
    hidden const ZERO_TIME2 = "00";
    hidden var distanceUnits = System.UNIT_METRIC;
    hidden var distance;
    hidden const ZERO_DISTANCE = "0.0";
    hidden var kmOrMileInMeters = 1000;

最初の2つ、mValueとvalueFormat はデフォルトの変数ですが、後の方で書くようにデータフィールドの区切りが1つでない時にエラーにならないように一応残してあります。本来の機能には不要です。
その次の3つがラベル用の変数で、それぞれ、文字列の距離、時刻、タイムです。
clockTimeが現在時刻用の変数で、eTimeが経過時間用の変数。ZERO_TIMEとZERO_TIME2はアクティビティーがスタートする前の初期の状態で画面に表示するための定数です。distanceUnitsは距離を表示するための単位(今回はkm)。distanceは距離用の変数。ZERO_DISTANCEは経過時間の時と同じく初期状態表示用の定数です。初期状態では変数にnullが入ってしまうので定数を指定してnullの時に表示するようにしています。kmOrMileInMetersは距離をkmに換算するための計算用の値です。距離はmで得られるのでkmに換算するのに1000で割って計算します。

初期化関数

デフォルトのままです。mValueを使わない場合は2行目は不要です。

function initialize() {
    DataField.initialize();
    mValue = 0.0f;
}
計算

どうもこのcomputeという関数と、後で出てくるonUpdateの関係が良くわかっていないのですが、とりあえず、アクティビティー中の更新されたデータをここで変数に入れるように使うようです。なので、現在時刻、経過時間、距離をそれぞれの該当の関数に代入しています。経過時間は最初info.elapsedTimeとしていたのですが、こうするとポーズを入れても時間がどんどん進んでしまいます。自分の場合、ランやトレランで休憩したときにポーズして時計を止めることはしないのでどちらでもよいのですが、一応ポーズすると時間が止まるinfo.timerTimeの方を選んでいます。

function compute(info as Activity.Info) as Void {
        // See Activity.Info in the documentation for available information.
        
    clockTime = System.getClockTime();
    //eTime = info.elapsedTime;
    eTime = info.timerTime;
    distance = info.elapsedDistance;
        
}
Obscure
function isSingleFieldLayout() {
    return (DataField.getObscurityFlags() == OBSCURE_TOP | OBSCURE_LEFT | OBSCURE_BOTTOM | OBSCURE_RIGHT);
}

これもよくわかっていないのですが、APIを読むとgetObscurityFlagsの説明は以下のようになっています(googleで日本語に翻訳)。

隠されている画面領域を取得します。
非長方形の画面では、画面の特定の部分が隠れます。たとえば、丸いスクリーンは正方形のスクリーンの角を効果的に切り落とします。このメソッドは、デバイス上の隠された画面領域に一致する、WatchUi.DataField.OBSCURE_* 定数によって定義された列挙値の合計を返します。このメソッドの使用は、 onUpdate()の呼び出し中にのみ有効です。

なので、ウォッチのような丸いスクリーンだと結果がtrueになるのかと思われます。この関数は後でonUpdateの所で呼び出しています。

アップデート

onUpdateは長いので少しずつ説明します。
まず、関数内で使用する変数の定義です。画面の幅をwidth、高さをheightに代入しています。また、textCenterには、テキストを真ん中に寄せる設定を入れています。backgroundColorには背景色を入れています(今回は白)。
次の2行が画面消去で、その次の3行が画面の区切り線の描画です。色は青です。

function onUpdate(dc as Dc) as Void {
    var width = dc.getWidth();
    var height = dc.getHeight();
    var textCenter = Graphics.TEXT_JUSTIFY_CENTER | Graphics.TEXT_JUSTIFY_VCENTER;
    var backgroundColor = getBackgroundColor();
    // set background color
    dc.setColor(backgroundColor, Graphics.COLOR_TRANSPARENT);
    dc.fillRectangle (0, 0, width, height);
    // set foreground color
    dc.setColor(Graphics.COLOR_DK_BLUE, Graphics.COLOR_TRANSPARENT);
    dc.drawLine(0,85,width,85);
    dc.drawLine(0,165,width,165);
        
    dc.setColor((backgroundColor == Graphics.COLOR_BLACK) ? Graphics.COLOR_WHITE : Graphics.COLOR_BLACK, Graphics.COLOR_TRANSPARENT);     

次が距離と時刻と経過時間の表示の部分です。
まず、現在時刻をtimeStringの文字列に代入します。
durationは経過時間の時、分、表示用の変数で、duration2が経過時間の秒、表示用の変数です。秒は小さいフォントで表示するので変数を分けています。
その次が画面全体のレイアウトの部分です。先ほど定義したisSingleFieldLayoutを呼び出して、trueならif文の中を実行してレイアウト表示をします。距離、経過時間、時刻のラベルを表示しています。現在時刻の表示もここで行います。
distStrは距離用の変数です。その次のif(distance==Null)の所が上で説明したアクティビティースタート前で距離がゼロの時Nullになっているときの処理です。elseの所が距離が何等かあるときの処理で距離数をkmに直して先ほどのdistStrという変数に文字列で代入しています。その下のdrawコマンドが距離の表示です。
その下からが経過時間表示の処理で、eTimeに何らかの数値が入っていれば経過時間に文字列を代入します。このとき、上で書いたように時、分と秒で分けて文字列を作成します。最後のelseの処理は数値がなくNullの場合で、初期値用の文字列を代入します。
最後の2行のdrawコマンドで経過時間を表示しています。

    var timeString = Lang.format("$1$:$2$:$3$", [clockTime.hour, clockTime.min.format("%02d"), clockTime.sec.format("%02d")]);
    var duration;
    var duration2;
    // do layout
    if (isSingleFieldLayout()) {
        dc.drawText(width/2, 30, Graphics.FONT_TINY, label, textCenter);
        dc.drawText(width/2, 180, Graphics.FONT_TINY, label2, textCenter);
        dc.drawText(width/2, 210, Graphics.FONT_NUMBER_MILD, timeString, textCenter);
        dc.drawText(width/2, 100, Graphics.FONT_TINY, label3, textCenter);
        dc.drawText(190, 70, Graphics.FONT_TINY, distanceUnits == System.UNIT_METRIC ? "km" : "mi", textCenter);
        var distStr;
        System.println(distance);
        if (distance == null) {
            distStr = ZERO_DISTANCE;
             }
        else {
            var distanceKmOrMiles = distance / kmOrMileInMeters;
            distStr = distanceKmOrMiles.format("%.1f");
        }
        dc.drawText(width/2, 60, Graphics.FONT_NUMBER_MEDIUM, distStr, textCenter);
        if (eTime != null && eTime > 0) {
            var hours = null;
            var minutes = eTime / 1000 / 60;
            var seconds = eTime / 1000 % 60;
                
            if (minutes >= 60) {
                hours = minutes / 60;
                minutes = minutes % 60;
            }
            
            if (hours == null) {
                duration = "0:"+ minutes.format("%02d");
            } else {
                duration = hours.format("%d") + ":" + minutes.format("%02d");
            }
            duration2 = seconds.format("%02d");
        } else {
            duration = ZERO_TIME;
            duration2 = ZERO_TIME2;
    } 
    dc.drawText(width/2, 130, Graphics.FONT_NUMBER_HOT, duration, textCenter);
    dc.drawText(220, 140, Graphics.FONT_NUMBER_MILD, duration2, textCenter);

最後は上にも書いたようにおまけですが、isSingleFieldLayoutがFalseの時(データフィールドが複数に区切られているとき)の処理で、label(ここでは距離)とmValueを表示しています。mValueは何も計算していないので常にゼロです。ここはレイアウトの紐づけを間違わなければ本来不要のはずです。

    
    } else {
        dc.drawText(width / 2, 5 + (height - 55) / 2, Graphics.FONT_TINY, label, textCenter);
        dc.drawText(width / 2, (height - 23) - (height - 55) / 2 - 1, Graphics.FONT_NUMBER_HOT, mValue.format(valueFormat), textCenter);
    }

ちなみにシミュレータでデータフィールドのレイアウトを3分割にするとこんな感じになります。

ウォッチ側で3分割を選択した場合。

やっぱり3つとも同じ表示になってしまいます。

だいぶ長くなったので今回はここまでにしようと思います。次回は、実機に作ったデータフィールドを転送するやり方と、また別のデータフィールドについて書いてみたいと思います。

一人北摂フォトロゲ

12月に行われた北摂フォトロゲのお手伝いに行った時に地図をもらっていたので、その地図を使って、一人でフォトロゲをやってみました。
フォトロゲとは、フォトロゲイニングの略で、ロゲイニングというのは、地図を持って、地図に書いてあるポイントを回って制限時間内にどれだけ多くのポイントを取れるかを競う競技です。オリエンテーリングをやったことがある人なら、スコアオリエンテーリングのようなものと言ったらわかりやすいかもしれません。
フォトロゲでは、ポイントに行ってもオリエンテーリングのようにフラッグが置いてあるわけではなく、パンチもありません。通過証明には現地に行って写真を撮ってくるという方法を使います。なのでフォトロゲイニングと呼ばれます。
北摂フォトロゲでは、ポイントでクイズやじゃんけんといった遊びの要素は全くなく、時間内に多くのポイントを集めるというのが競技の規則になっています。
また、ロゲイニングは通常2人以上のグループで参加するのが普通ですが、北摂フォトロゲでは一人で参加するソロのクラスもあります。競技の時間は3時間のクラスと5時間のクラスがあります。今回は5時間を想定してコースを組んでみました。優勝チームだと40km以上走るのだそうですが、さすがにそこまでの走力はないので、一応目安として12分/kmのペースで行くと考え25kmくらいのコースを組んでみました。また、本来のスタートゴールはJR高槻駅から京都方面に少し行ったところにある安満遺跡公園なのですが、自宅から安満遺跡公園まで出かけてスタートするのもだるいのでJR高槻駅をスタートゴールにしてみました。
そして、少し反則気味なのですが、あらかじめルートを組んでおいてそれをガーミンのForerunner255に入れておいて走りながらルートに乗っているかどうかを確認して走ってきました。少し反則と書いたのは、ルール上はスマホのアプリは使用OKなので、作戦タイムが20分あるのでその間に、Garmin Connectでルートを組んでウォッチに転送すればルール上は問題ないということになります。ただ、今回は、スタート前の20分間でもなく、アプリでなくPCでコースを組んだのでやや反則という感じです。アプリでもできないことはないのかもしれませんが、やったことがないのでどこまで細かいルートが設定できるのかはわかりません。
まあ、ロゲイニングをしつつ、1月末の石舞台100へ向けてのトレーニングという意味合いもあるので、その辺はまあ良しとしようかと。

出だしでいきなりつまづく

JR高槻駅を9時頃にスタート、5時間の予定なので、午後2時にゴール予定です。ちなみに、大会の本番では、タイムオーバーは1分毎に100ポイントの減点とかなり厳しいルールになっています。
さて、最初のポイントは不老水です。能因法師が不老不死を願って煎茶にして飲んだ水だとか。今は飲めないそうですが。
ポイントは18点です。ポイントは、スタート地点からの距離や登りなどに応じて割り振られています。当然遠いところが高い得点です。ここは近場なので非常に低い点数ですが、通り道なので寄っていくことにしました。
ところが、住宅地の中で道が入り組んでいてどこにいるのかわからなくなってしまいました。はじめは、駅からまっすく北に向かって天神さんの所を右に曲がって天神さんの脇を巻くように北上したところまでは良かったのですが、そのあとが道がわからず、マップとスマホのアプリの地図を見比べてなんとか現在地を割り出してたどり着くことができました。オリエンテーリングでは反則の方法です。まあ、ロゲではOKなんですけど。オリエンティアとしてはちょっと情けない。ただ、言い訳を書いておくと、ロゲの地図はオリエンテーリングの地図と違って道がとても見づらいのです。オリエンテーリングではある程度の太さの以下の道は全部同じ太さで書かれていて、実線と点線などの区別であらわされていて、道が非常に見やすく工夫されているのですが、ロゲの地図は国土地理院の2万5千分の1の地図をベースに書かれていて、しかも細い道は本当に細く書かれていて老眼にとってはものすごく見づらいものになっています。また、縮尺もオリエンテーリングでは1万分の1や7千5百分の1といった縮尺を使うのでかなり見やすいのですが、ロゲでは使うエリアが広いのでどうしても2万5千分の1などの縮尺になってしまうので全てが小さく書かれていて非常に見づらく、特に住宅街などの入り組んだところは非常に苦労するということが今回わかりました。
17分ほどかかってようやくポイントを発見。平地にもかかわらずkmあたり10分越えのスローペース。

最初のポイント不老水

次は、名神を越えた先にある神峯山寺への道標です。古曽部の運動公園の脇を抜けて名神まで登り、名神を越えてまた下って、川沿いを北上したところにありました。ここは比較的簡単にたどり着くことができました。ここのポイントは26点です。

神峯山寺への道標

次はタチソ地下壕跡です。道標のあった川沿いの道を北上し、弥生が丘の住宅地の入り口付近です。ここもすんなりと行くことができました。
タチソ地下壕とは、戦時中に計画された工場の跡らしいです。工事中に終戦になって使われることはなかったらしいですが。ここは29点です。

タチソ地下壕跡

だらだら登りに苦しむ

その次は、最高得点のポイント、神峯山寺の仁王門です。ここは102点です。
タチソ地下壕から川沿いに進み、そのあと新名神を右手に見ながら進みます。このあたりからのぼりが始まります。そして、原からのポンポン山への登山道と合流し、しばらく登ると神峯山寺に到着です。ポンポン山の登山道は何度か通ったことがあるのでここは簡単でした。

神峯山寺の仁王門。正月だからなのか飾りつけされていました。

ここで、トイレ休憩。今日はどうもトイレが近い日らしく、スタート前にいっておいたのですが、ここでも行くことに。タイムロスなのですが仕方ありません。
次のポイントは諏訪神社です。諏訪神社へは、ポンポン山への登山道をしばらく登って、途中で分岐を右に進み、またひたすら上る。そして少し下ったところの集落にありました。集落の中の道がえらい狭くて、人が2人並んで通れないんじゃないかという細さで、入り口を入るのを躊躇しましたが、問題なく到着。
ここは68点です。

諏訪神社。集落の奥まったところにある。

実は、諏訪神社のすぐ近くに77点のポイントがあるのですが、タイムと距離のことを考えて今回はパスしました。競技を真剣にやっている場合、ここまでの経過時間を見てどうするか判断することになるのでしょう。
次は、大沢の杉です。諏訪神社から元来た道に戻って、柳谷へ向かう道をまたひたすら上ります。ここまでの登りはほとんどが急勾配と呼べるようなところがなく、走ろうと思えば走れるところなのでかえってしんどい。どうもこういう登りは苦手なんです。でも、トレーニングのために来ているという側面もあるので頑張って登ります。登りの途中で腸腰筋が痛くなってきました。もう少し鍛えておかないとだめなようです。まあ、筋肉痛なので我慢できる範囲なので我慢しながら進みます。上り坂を上り切ったあたりに大沢の杉への入り口がありました。車道から右に入って斜面を登ると本当に立派な杉が鎮座しています。ここは83点です。

大沢の杉。ここまで立派な杉を実際に見たのは初めて。

ようやくトレイルに

大沢の杉の次は、トレイル途中にある119番通報ポイントB-12です。ハイキングコースなんかによくある、迷ったときに今どこにいるかを消防に連絡する例の奴です。大沢の杉から元来た車道に戻って、しばらく進んで、柳谷まで行かずにギロバチ峠への道を右に入ります。最初は柳谷観音を回るルートを考えていたのですが、そこまで行くと距離が30kmを越えてしまいそうなので手前で曲がって引き返すルートにしました。
ギロバチ峠への道からようやく本格的なトレイルに入りました。峠まではすぐで、あとはひたすら下りです。下りの途中に119番通報ポストがありました。ここは71点です。

トレイルの途中にある119番通報ポスト

次は若山神社です。トレイルをひたすら下って、柳谷から下ってくる車道と合流し、しばらく下った集落のあたりからまたトレイルに入りなおします。ここで、本来は集落の手前の橋を渡ると近道だったのですが、道が読み切れずに、1つ橋を行き過ぎて2番目の橋を渡ってしまい、かなり大回りなルートになってしまいました。本当に集落のあたりはわかり難い。
ここのトレイルは比較的平坦で若山神社に到着。僧行基文武天皇に勘定したのが始まりとか。結構立派な神社で参拝客も何人かおられて、おみくじやお守りを売っている窓口もありました。ここで、またトイレ休憩。そして、お昼近くになって来たし、日差しの当たるところにベンチがあったのでここで昼食のおにぎりを2つ食べる。ここは54点。ゴールに近くなってきたので点数が下がってきました。

若山神社

最後の登り

若山神社からは太閤道を通って、ベニーカントリークラブの淵まで一気に登ります。ここが今日のコース一番の急登でした。まあ、それほど長くないのですが。
ただ、ベニーカントリーまで行けば登りは終わりかと思っていたら、ゴルフコースの淵の道は結構アップダウンがあって、こっちの方が精神的にきつかったです。
ゴルフコースの淵の道を外れて南下してしばらく行ったところにある展望所が次のポイントです。ここが56点。

島本の展望所。遠くに三川合流地点が見える。

その次は安満宮山古墳です。展望所からトレイルを進んで、若山まで登り、そこから降りると高槻市営の公園墓地に裏から入る形になるのですが、途中で少し北西にわき道を入ったところにまた別のポイントがあって、ここも競技だったら残り時間を考えて寄るかどうかを検討する場所になりそうなところでした。今回は、とりあえず25kmを走るということを目標にしていたのと、足の痛みを抱えていたので予定通りパスして安満宮山古墳に向かいました。

コースの途中にあった若山。ここはポイントではないですが。

若山からトレイルを下ってくると高槻市営公園墓地に裏から入る形になります。古墳は高槻市営公園墓地の中にあります。この古墳は3世紀ごろのもので、卑弥呼が魏の皇帝からもらったといわれている三角縁神獣鏡も出土したそうです。ここが38点。

安満宮山古墳。
古墳から見た高槻市内の風景

さて、次が最後のポイントです。得点はなんと6点。安満遺跡公園のすぐ北にあるJRの踏切です。
墓地の中を降りてきて、名神新名神のジャンクションの所から、しばらく川沿いに進んで、そこから南下すればよいのですが、ここもまた市街地になって、どうも現在地がつかみづらい。まあ、だいたいの位置はわかっているので、適当に南下できる道をたどっていったらドンピシャで踏切に到着。
この踏切には、警報機もなく、自動で降りてくる遮断器もありません。遮断機はあるにはあるのですが、手で上げ下げするという変わった踏切です。
何故こうなっているのか詳しいことはわかりませんが、ここで横断するのは車庫へ続いている線路で、車庫へ向かう列車は速度を落としてゆっくり進むので、運転手が目視で確認しながら通るので問題ないということなのかもしれません。その脇にある東海道線の線路には、ちゃんと警報機と自動遮断機がついています。

JR高槻駅の近くにある手動遮断器

ゴールへそして結果は?

遮断機が最後のポイントで、後はゴールするだけです。ゴールはスタートと同じくJR高槻駅に設定したので、駅に向かいます。本来なら遮断機から元来た道を引き返して、西国街道を進むのが早いのですが、せっかくなので踏切を渡って南側に出て、安満遺跡公園との間の道を進み、八丁畷の交差点から北上してきた道とぶつかるところで、線路を渡り、線路沿いにしばらく進み、西国街道に一旦合流。そして、しばらく西に進んでまた、左に折れて線路沿いに進んで高槻の駅に到着。
タイムは4時間25分14秒。ということで35分ほど早く着いてしまいました。結果論ですが、あと一つくらいはポイントを取れていたと思います。この辺が競技の本番になると難しいところだと思います。タイムオーバーすると激しく減点されるので絶対に時間内にゴールしないといけないのですが、余裕を見すぎると今回のようにかなり時間が余るということになってしまいます。
得点は593点でした。優勝チームは1000点を越えているので全然ですね。まあ、それでも1月末の石舞台100はもっとアップダウンが激しいですが距離は35kmなので、今回25kmを走ったことは大会前の練習としてはまずまずだったのではないかと思っています。

1年の計は元旦にあり

あけましておめでとうございます。
今年は、子供が大学受験ということで、久しぶりに我が家で過ごす正月となりました。なので、あまり正月気分がないですが。
とはいえ、1年の計は元旦にありとか。ということで、昨年は何を書いたか見返してみると、目標にトレランで50km、ウォーキングで100kmと書いていましたが、結局どちらも実行することはできずじまいでした。ウォーキングは結局イベントに参加せず。個人的にも長距離のウォーキングはやらずじまいでした。トレランの方は弘法トレイルの44kmを完走したので、まあ近い線まで行ったかなというところ。
さて、今年の目標は、やっぱり、トレラン50kmにウォーキング100kmとしておきます。トレランでは、今年は比叡山国際トレイルランの50kmにエントリーする予定です。制限時間が11時間と厳しいので完走できるかどうかはわかりませんが、今年の第一目標として完走を目指したいと思っています。
また、来年にはUTMFのKAIにチャレンジしたいと思っているので、それに向けて秋頃に100kmウォーキングをやりたいと思っています。今のところ、木曽路の100kmが時期的にちょうどよいかなと思っています。

プロフィールを更新しました。

昨年で還暦になって、今年は61になりますが、プロフィールがブログを始めたときのまま、還暦までに100kmを目指すことになっていたので、更新しました。
プロフィールの所にも書きましたが、最近はトレランで100kmはちょっとあきらめモードです。というのは、走っていると胃がなかなか食べ物を受け付けないので長時間のレースとなると最後まで持たないような気がしています。年のせいなのか、元々そういう体質なのかわかりませんが、たいてい長距離のレースをすると、終わってしばらくするとお腹がすいてくるというパターンで、半日程度のレースなら何とかなるのですが、丸1日かかりそうな100km程度のレースとなるとそんな感じではハンガーノックに陥って動けなくなるのではと思っています。まあ、やってみないとわかりませんが、とりあえず100kmウォーキングをやってみてその時の体の反応を確かめようかとは思っています。

今日は、とっても短い文章になってしまいましたが、本年もどうかよろしくお願いします。

やればできる! 5分/km

比叡山国際トレイルを完走するためにスピードをもう少しつけないといけないということで、久々にペース走をやってみました。
ペース走は本当に久しぶりです。たぶん、ロードレースにまだ出ていた30代の頃以来ではないでしょうか?
GarminのForerunner255はペース走の機能があるのでこれを使ってみました。さすがは、ランニング用ウォッチ。
やり方は簡単です。
Garmin Connect(アプリでもPCでもどちらも可)で、トレーニングと計画→ワークアウトと進んで、ランワークアウトを作成、そうするとウォームアップとクールダウンに挟まれたランワークアウトができるので、ワークアウトを編集、タイプの所でペースを選択し、ペースを設定し、目標の距離を入力すれば出来上がりです。今回はペースを6分/km、距離を5kmとかなりぬるめの設定にしました。ペースを6分/kmとしたのは、最終的には15kmくらい同じペースで走り続けられるようにと思ったからで、とりあえず今回は手始めとして5km走ってみようということでした。本当に久しぶりなので。

作成したワークアウト

ワークアウトができたら、デバイスに転送し、アクティビティメニューでランを選択、UPボタン長押しで、トレーニング→ワークアウトと進んで、さっき作ったワークアウトを選択してスタートすれば始まります。
自分の場合は平地で走るときはだいたいいつも近所の河原に行って走るので、そこまでの行きをウォームアップ、帰りをクールダウンとしました。スタートするとウォームアップが始まります。河原に着いたところでラップボタンを押すと、ペース走の始まりです。
GPSで距離とペースを測るので、今のペースが速いか遅いかを判断してガーミンが教えてくれます。ところが、始めガーミンに遅い、遅いと言われてペースを上げて走ったら5分/kmくらいのペースになってしまいました。どうも、ペースを設定するときにきっちり6分/kmにしたくて、幅を持たせずに最低と最高をどちらも6分/kmにしたのが良くなかったようです。少し幅を持って5分50秒~6分10秒という感じにした方が良かったのではないかと思います。
それにしても、最近は5分/kmで走り続けるということは全くしていなかったので最初はきついと思ったのですが、ペースに乗ってしまうとそのままのペースで走ってしまいました。5km持つのかなと思いつつ、まあいいやと思って走っていると5kmまでほぼ全てのラップで5分/km以内で走り切ることができました。

今回のペース走のラップ

1-2kmの所で5分/kmを少し超えていますが、最初の1kmが設定よりも早すぎたので6km/分に落とそうと思って少しペースダウンしたので少し超えています。結果的にはこの時も一回遅いと言われてペースを上げてまた5分/kmよりも早いペースに戻っています。河原を走っているので多少のアップダウンがあるのですが、基本的に行きが下りで帰りが登りなので、帰りの方がちょっとペースが速い感じです。これは、体がこのペースに慣れて少し楽になったからだと思います。今日の感じだと5kmよりももう少し同じペースで走れそうな感じでした。5分/kmは今の自分にはかなり速いペースと思っていましたが、やればできるもんだと、ちょっと自信がつきました。まあ、昔は4分/kmでハーフマラソンくらいは走っていたことを考えるとずいぶん遅くなったという気がしますが、年を考えれば十分できたかと思います。なので、最初6分/kmで距離を伸ばしていこうかと思っていましたが、5分/kmでもう少し頑張ってみようかと思っています。

比叡山国際トレイルランの日程が発表されていた

来年の比叡山国際トレイルランの日程が発表されていました。公式ホームページを何度か確認していて2023年バージョンから全然変わってなかったので、来年本当にやるのか心配になっていました。今年は昨年の12月23日から申し込みが開始されていて、その日を今年は過ぎてしまっていたもので。
ホームページの下の方にあるFacebookのリンクをクリックするとFacebookのページの方に予告が出ていました。
それによると、開催日は5月11日(土)、エントリーの開始は1/26(金)の夜8時となっていました。石舞台100の前日ですね。今年よりも1か月以上申込期間が後ろ倒しになっていました。色々と準備の都合もあるのでしょうが、ちょっと心配しました。

護王神社に行ってきました。

金曜日に京都市内に出張したのですが、たまたま通りすがりにあったので、護王神社に行ってきました。

護王神社

写真を取り忘れたのでパンフレット写真を載せておきます。
場所は、烏丸通り沿い、御所の西で、丸太町と今出川の間です。

神社の案内とアクセス

こちらもパンフレットを写したものです。
パンフレットに書いてあるように、和気清麻呂公を祭った神社で、清麻呂宇佐神宮にご神託を聞きに行くときに、足萎えだったのがイノシシに助けられて治ったという故事にちなんで、足腰の神様となっているとのことです。境内には、狛犬ならぬ狛猪があったり、イノシシの置物がたくさん置いてあったりとイノシシパラダイス状態でした。
足腰にご利益があるということで、足腰の無事を祈願したあと、お守りを買って帰りました。

護王神社で買った足腰に効くというお守り。

今年は腰、膝の痛みに悩まされたので、今度からレースの時はこのお守りをもって走りたいと思います。

Garminウォッチフェイス用ウォッチフェイスを作る(その8)

前回で完成と書いてしまいましたが、どうも問題があったようです。
alasixosaka.hatenablog.com
前回のウォッチフェイスを実機にインストールしてしばらく動かしていたら、いつの間にやらパーシャルアップデートが止まっていました。
何かのタイミングで規定時間の30msを越えてしまったらしく、秒針が止まっていました。手に持つとアクティブになってフルアップデートに入るのでしばらく動くのですがローパワーモードに入ると秒針が止まるという状態になりました。
そこで、以前ConnectIQストアからダウンロードしたウォッチフェイスの動作を確認したところ、スマホとの接続に関してはフルアップデートの時のみ情報を見に行っているようでした。
つまり、System.getDeviceSetting()を実行するとタイミングによっては30msを越えてしまうのではないかということがわかりました。まだ、推定の段階で実証できていませんが。実機でもスマホ側のBluetoothを入れたり、切ったりしてスマホとの接続を入、切して、問題なく表示されて秒針も止まらないことを確認していたのですが、System.getDeviceSetting()では色々な情報をゲットするので情報の種類や量がその時によって違ってしまい、所要時間が異なるのではないかと推定しています。
ですので、一旦、スマホとの接続確認はフルアップデートの場合のみにして、onUpdate()の所でチェックして表示するように変更し、しばらく様子を見ることにしました。