M5StickC Plusを使って、時計の中でも特に、腕時計⌚をイメージした物を作ろうと思います。M5StickC Plusなので、WiFiには接続できるのですが、スマートウォッチと言えるほどの物ではありません。普通、カレンダ機能付きの腕時計は、今日が何月何日か?を表示することができるようになっています。
今回作るカレンダ時計は、単に「○月○日、○時○分○秒」等と表示するだけではなくて、今月のカレンダを全体表示した上で、
を、直観的に感じられる様な腕時計にしたいと思います。
M5StickC PlusのTFTディスプレイは小さくて狭いので、ひと月分のカレンダを表示すると、もう、いっぱい、いっぱいです。あくまで、カレンダ表示を主として、現在時刻および、バッテリ残量表示は二の次(おまけ)とします。おまけなので、表示サイズは小さいけれど、ちゃんと表示してあるので、腕時計としては、機能するはずです。
あと、実用的に腕時計として使うには、M5StickC Plus用の「腕時計アクセサリ」等が必要となります。ちなみに、筆者は持っていません……m(1^_^;)m。
一般的に、ひと月分のカレンダは、横方向に曜日、縦方向に週が並び、そこへ日付を1から当該月の最終日まで、左から右/上から下の順で埋めることで表示されます。従って、カレンダを描画するのに必要な情報は以下の二点です。
これまで本HPにて解説済みの技術を使って描画が可能です。上に張っておいたリンク先を参照して見てください。それほど難しいことではないので、UIFlowを使って実現して見ようと思います。
ただし、残る課題として、祝日の表示を挙げることができます。
公式や一定のルールに当てはまらないので、各年の祝日データを用意して、描画しなければなりません。
拙作iConClock(Windows版ソフトウェア)も、同様に各年の祝日をCSVデータとして配布しています。
CSVデータは、毎年2月に筆者が更新しています。このCSVデータは、iConClockのHPからダウンロードできるようになっているので、CSVデータをインターネットから取得して使うと、カレンダに祝日データを反映することができます。
CSVデータは、筆者が毎年更新しているので、ご使用になる皆様から見ると、「自動的に毎年の祝日データが更新される」様に見えるはずです。
インターネットのWebサイトからデータを取得するには、HTTPブロックを使います(下図)。
HTTPプロトコルについては、こちらをご確認ください。データの取得には、GETメソッドを使います。上図のHttp Requestブロックを配置して:
HTTPリクエストが成功したら、Get Dataブロックを使うと、取得したデータを参照することができます。聞くだけだと、簡単ですよね?でも、このブロック、少し癖があって、そう簡単には行きませんでした。
iConClockのHPから、祝日のCSVデータダウンロードコーナを確認して、CSVデータのURLをHTTPブロックに指定しました。
HTTPブロック自体は成功するので、「Success」内のコードが実行されるのですが、この中でGet Dataブロックを実行すると、エラーとなってデータが取得できません。
何でかな?と、色々試してみたところ、どうやら、S-JIS符号化されたテキストデータが嫌われている模様です。試しに2023年のCSVデータだけをUTF-8で符号化してみたところ、正常に取得できました。
ふぅ〜ん。
でも、筆者は反unicode派なので、データをUTF-8に変更するつもりは一切ありませぬ。
S-JISコードは、「元日」など、祝日の名前を日本語で記載するために使っているのですが、いま必要なのは、祝日の日付データだけです。日付データは、普通にASCIIコードで記載してあるので、S-JIS部分は無視して取り込んでくれたらそれで良いのだけど、エラーではじくというのは頂けない。
まあ、もう少しあがいてみましょう。
時に、前節で示したHTTPブロックのSuccessとFailには注意、と言うお話です。
HTTPリクエストを投げると、正常な反応としては、ステータスコード=200番台が返ってきます。一方、エラーが起きると、400番台、500番台のエラーステータスが返ってきます。
このイメージでSuccessとFailを考えては、だめです。
よく見るエラーステータス=404 not found、なんて、使う側から見れば「読み出し失敗(Fail)」なのですが、HTTPブロックにとってはSuccessだからです。
「HTTPブロックは正しく実行できて(Success)、ステータス404が返って来た」って解釈みたいです。
なので、ステータスが200番台であれ、400番台であれ、Successが実行されます。HTTPブロックを使う場合には、Success処理内で、Get Status Codeブロックを使ってHTTPリクエストのステータスコードを確認し、エラー処理を別途実行してください。
更に更に、完成したUIFlowプログラムをPythonのコードで確認してみると、HTTPブロックのSuccess/Fail分岐には、try〜exceptが使われています。これは、tryからexceptまでの間で致命的なエラーが発生して例外(exception)が投げられた場合に、except以下のコードが実行されるステートメントです。
要は、HTTPブロックの実行に際して、致命的なエラーが発生して例外が投げられた時、初めてFailが実行される訳です。が、try〜exceptの間には、Success内の処理も記載されています。つまり、Success処理内で、HTTPリクエスト以外の致命的エラーが発生した場合にも、Fail処理に飛んでしまうのです。
実は、S-JISコードが符号化できなかったGet Dataブロックが例外を投げるのですが、この時Success処理とFail処理が両方とも実行されている様に見えて、大混乱してしまいました。
そういう物(仕様)だと判っていれば、対処方法はあるので、単なる注意喚起です。
話を元に戻して、S-JIS符号化されたテキストデータの中から、ASCIIコードで記載された部分のデータだけを取り出して使う事はできないのか?あがいてみます。
iConClockのHPから取得したCSVファイル(S-JIS符号化)がエラーとなるのは以下の場所です。
そもそもUIFlowはブロックで作ったプログラムをPythonというスクリプト言語に変換して実行しています。上のコード部分をPythonのコードで確認してみると、以下の様な記述に変換されています。
lst = str (req.text);
HTTPブロックで取得したHTTPデータは、reqというクラスで受けられています。そこからtextメソッドを使うと、取得したデータを参照できる、と言うのが通常の流れです。
実際、取得したデータがUTF-8符号化されている場合、この部分はエラー無く正常に処理されます。ところが、取得したデータがS-JIS符号化の場合、エラーとなって例外が発生します。
UIFlowではこれ以上何もできないのですが、Pythonの言語仕様を調べてみると、HTTPブロックの文字コード処理前の生データが、contentメソッドで取得できることが判りました。文字コード(S-JIS等)を処理する前のデータを取得できるので、「req.content」はエラー無く実行できました。
そうと判れば、下図のExecute codeブロックを使って、Pythonのコードを直接記述することで回避できるかもしれません。
※Python言語使用を理解している必要はあるけど、UIFlowが対応していない機能でも、Pythonには実装されているケースが有るので、Execute codeブロックは中々有用。
以下図の様に使います。
上のブロック実行後、変数lstには、バイト文字列を表す「b'〜'」という文字列が追加され、「〜」の部分に受信したデータが格納されています。後は普通に文字列処理で、CSVデータの中から所望の祝日データを抜き出すだけです。
S-JIS符号化された文字列は諦めるとして、CSVファイル中の日付データを吸い上げたいと思います。
CSV(Comma Separated Values)は、カンマ(,)で区切られたデータ形式です。
同様に、HTTP GETしてきた祝日のCSVデータには、改行コードが含まれていて、これが厄介です。
前節のExecute codeブロックで、「lst=req.content」したことで、改行コードなどの非印刷可能文字は「\r\n」等の文字列に変換されています。
※CR+LFの様なコードではなく、「\r\n」という文字列です。
そこで、改行コード(「\r\n」)を、カンマ(,)に置換して、処理を簡略化したいと思いました。文字列中の特定の文字を置換する処理は、UIFlowではreplaceブロックを使って以下の様に実行することができます。
これはこれでOKでした。contentメソッドを使って、S-JISデータを取得した場合に適応できます。
ただ、取得したデータがUTF-8で符号化されていた場合、「req.text」で取得したデータに対して同じ処理を使用とすると、それはダメでした。なぜダメなのか?Pythonのコードで確認してみると、以下の様に変換されていました。
lst = str(lst).replace('\\r\\n', ',')
特殊記号「\」が打ち消されて「\\」しまっています。やりたかったのは、以下処理です。
lst = str(lst).replace('\r\n', ',')
UIFlowが勝手に「\」を打ち消して「\\」に変えてしまっている様です。そういう仕様なのだろうから、バグでは無いけど、やりたいことは「これぢゃない」です。
これもExecute codeブロックを使って解決することができます(下図)。
「ブロックを使ってGUIでプログラムが組めますよ」が売りのUIFlowで、一々Pythonのコードを確認しながら、Pythonコードを直接指定してプログラムを組むなんて本末転倒です。しかし、たま〜にC言語からインラインアセンブラでプログラムを記述して、C言語だけでは実装不可能な機能を記述するのと同じだと思えば、機能としては、こういう機能が有って良かったな、と。
ともあれ以上の処理で、改行コードを含むCSVデータに於いて、改行をカンマ(,)に置き換えた文字列がlstに入りました。ここで、make with delimiterブロックを使って、CSVデータをリスト形式に変換します(下図)。
これで漸く、CSVデータをUIFlowのリスト型に変換できたので、リスト型データを操作(下図、LISTSブロック)しながら、日付データを吸い上げればOKです。
完成したプログラムを以下にリンクしておきます。
完成プログラム:caln_watch.m5f
オンライン版UIFlowから読み込み、M5StickC Plusへ書き込んでお使いください。本プログラムではHTTPで祝日のCSVデータを取得していますが、インターネットに接続する処理は記述していませんので、オフライン版UIFlowでは実行できません。
なお、setupブロックの先頭付近で定義している、変数mon_startによって、カレンダの週表示を日曜日スタートにするか、月曜日スタートにするか、選択できるようになっています。
月曜日スタートの方が好みなのですが、日曜日スタート派の方は、設定を変更してお使いください。
拙作iConClockサポートの一環として、S-JISの祝日CSVデータは今後も更新して行く予定です。ので、「皆様、使う側」から見ると、毎年自動的に祝日データが更新されて、カレンダに表示されるように見えるはずです。
では、実験です。以下図に実験中の様子を示します。
※上図では「山の日」と日本語表示されていますが、2023年の祝日CSVデータのみ、UTF-8で用意したためです。他の年ではS-JISデータしか用意しないので、何の祝日なのか?については表示されません。あしからず。
カレンダの表示が主役のプログラムなので、日付や時刻を変更しながら動作を確認しました。このためのデバッグ用コードがrtc_ntp関数の中に入っているので、ご確認ください。
等、ざっと確認済み、問題なしです。
ただ、「秒」が時々飛んで表示される事が有ります。Pythonの処理速度が遅いので、loop内の処理が1秒以内に終わらないケースが有り、その場合に「秒」表示が飛んでしまう様です。致命的な問題では無いと判断したのと、Python処理速度の問題なので、放置してあります。
※「飛ぶ」とは?……10,11,13,14,15,……の様に、この例だと、「12秒」が表示されずに飛んでしまう事を指しています。
気になるようなら、秒表示を削除して、分表示止まりの時計に改造してみてください。あるいは、割り切って5秒毎に表示を更新するとか。普段の生活で、正確に秒まで知りたいケースは少ないはずです。女性用のアクセサリを兼ねた、おしゃれな腕時計には、そもそも、秒針が付いていない物もあるので、それと同じ使い勝手となります。
M5StickC Plusを本当に腕時計として使用するには、もう一つ、バッテリ容量の問題をクリアしないとならないので、実用に使うのはちょっと……、な、感じではあります。まあ、UIFlowでHTTPプロトコルを使うサンプルとして、ご覧ください。