工作と競馬2

電子工作、プログラミング、木工といった工作の記録記事、競馬に関する考察記事を掲載するブログ

javascript自分用覚書


日時関連

日時便利関数集クラス

  • 指定した分だけ日時をシフトしたDateオブジェクトを作成
  • Dateオブジェクトからyyyy-mm-dd
  • その日時の0時0分0秒のDateオブジェクトを取得
class DateUtil {

    // 指定した分だけ日時をシフトしたDateオブジェクトを作成
    // dt: 元時刻(Dateオブジェクト)
    // days, hours, minutes, seconds: シフト量(integer)
    // return: 新時刻(Dateオブジェクト)
    static get_shifted_Date(dt, days=0, hours=0, minutes=0, seconds=0) {
        return new Date(dt.getTime() 
            + days * 1000 * 86400 
            + hours * 1000 * 3600 
            + minutes * 1000 * 60
            + seconds * 1000
        );
    }

    // 文字列(yyyy-mm-dd)
    // dt: Dateオブジェクト
    static date_to_yyyy_mm_dd(dt, delimiter="-") {
        var yyyy = dt.getFullYear();
        var mm = ("0" + (dt.getMonth() + 1)).slice(-2);
        var dd = ("0" + dt.getDate()).slice(-2);
        return yyyy + delimiter + mm + delimiter + dd;
    }

    // 0時0分0秒のDateオブジェクトを取得
    static get_daty_start(dt) {
        return new Date(Math.floor(dt.getTime() / (86400 * 1000)) * 86400 * 1000);
    }

}

Dateオブジェクト

Dateオブジェクト作成時にそのまま数値を足すと時刻がシフトするが、作成されたDateオブジェクトに数値をそのまま足してもシフトしない

// これは1日ずれたDateオブジェクトは取得できる
x = new Date() + 86400 * 1000; 

// これはDateオブジェクトにならない
x = new Date();
x = x + 86400 * 1000; 

// これは大丈夫
x = new Date();
x = new Date(x.getTime() + 86400 * 1000); 

UI関連

日時範囲を選択するUI

  • 各種ライブラリに依存せず(といってもjqueryには依存)
  • 日付が前後が逆転しないようにしてある
  • 日付を操作したときに発火するコールバックを設定可

↓ 描画イメージ

// 日付範囲UI
class DateRangeUI {

    static create(id, change, option={}) {
        /**
         * 作成
         * id: UIに与えるID, 開始には{id}-start, 終了には{id}-endと付く
         * change: 日付変化時コールバック(change(args)),
         * option = {
         *  start: 先頭の日付(Dateオブジェクト, 任意, 指定しなければ現在時刻),
         *  end: 終端の日付(Dateオブジェクト, 任意, 指定しなければ現在時刻),
         *  changeArgs: changeの引数(object, changeの引数args.dataとして取り出せる, 任意)
         * }
         * return: $("<div>")
         */

        if (!("start" in option)) option.start = new Date();
        if (!("end" in option)) option.end = new Date();
        if (!("changeArgs" in option)) option.changeArgs = undefined;

        // startが変わったとき
        function onchange_start(args) {
            // 逆転直す
            if (new Date($("#" + id + "-start").val())
                - new Date($("#" + id + "-end").val()) > 0) {
                $("#" + id + "-end").val($("#" + id + "-start").val());
            }
            change(args);
        }

        // toが変わったとき
        function onchange_to(args) {
            // 逆転直す
            if (new Date($("#" + id + "-start").val())
                - new Date($("#" + id + "-end").val()) > 0) {
                $("#" + id + "-start").val($("#" + id + "-end").val());
            }
            change(args);
        }

        return $("<div>").append(
            $("<span>").text("表示範囲"),
            $("<span>").css("margin-left", "10px").css("margin-right", "10px"),
            $("<span>").text("開始"),
            $("<span>").css("margin-right", "5px"),
            $("<input>").val(DateUtil.date_to_yyyy_mm_dd(option.start)).prop({
                "id": id + "-start",
                "type": "date",
                "name": "start"
            }).change(option.changeArgs, onchange_start),
            $("<span>").css("margin-left", "10px").css("margin-right", "10px"),
            $("<span>").text("終了"),
            $("<span>").css("margin-right", "5px"),
            $("<input>").val(DateUtil.date_to_yyyy_mm_dd(option.end)).prop({
                "id": id + "-end",
                "type": "date",
                "name": "end"
            }).change(option.changeArgs, onchange_to)
        ).css("margin", "10px").prop("id", id);

    }
}

玉ねぎ

概要

材料

  • 玉ねぎ 中1個
  • ベーコン 薄切り10cm×5cm 4枚
  • ニンニク 1片
  • かつお節 適量

調味料

  • 油 大さじ1
  • 塩 適量
  • 味の素 適量
  • こしょう 適量
  • 醤油 小さじ2
  • みりん 大さじ1
  • 酒 大さじ1
  • 水 大さじ2

作り方

カット

  • 玉ねぎを厚さ7,8mmの輪切りにする
  • ベーコンを幅7,8mmの短冊切りにする

フライパン調理

  • フライパンに油とにんにくを入れて温める
  • 玉ねぎを入れ、軽く塩を振って軽く焼き目がつくまで焼く
  • 反対側も同様
  • ベーコンを入れて軽く焼く
  • 他の調味料を入れて、水分がなくなってきたら火を止める

  • 最後に、鰹節をかけて完成

しそを育てる2022(1) 種まき

概要

しその種まきを行った。



背景と目的

昨年に引き続き、しそを育てたい。昨年は、ある程度育ったものの、反省点があるので、それを考慮して育てていく。まずは、種まきから。



詳細

1. プランターと土の用意

昨年と同じプランター、土を使用する。土は、ベビーリーフを育てた後放置していたので改めて肥料(昨年と同じもの、30g)と水をあげた。やってみてわかったことは、土が相当乾いていて、写真のようにちょっと水をあげたくらいではちょっと掘り返すと全然湿りきっていないことがわかった。

土の容量は、7~8Lだが水は1.5Lくらいあげて、よく混ぜてちゃんと水が行き渡った感じだ。


2. 種まき

今年使用する種は、サカタの青ちりめんしそ。

www.sakataseed.co.jp

プランターの中心付近に1cmほどの溝を掘り、種をばらまき、土をかぶせた。昨年、いちいち発芽率を計算して個数を決めたが、結局のところ日当たりや水やり、そのほか土など様々な状況が影響するので表示上の発芽率など全くあてにならないことがわかったため、今年は個数を機にせずばらまくことにした。なので、発芽自体は昨年より確実にするだろうから、間引きが必要になるだろう。

※種は全く見えない。

最後に、自動水やり器のホースをセッティングして満遍なく水やりできることを確認した。また、成長記録用のカメラも設置した。



まとめと今後の課題

しその種まきを行った。発芽まで待つ。


田んぼ用リモートカメラ(4) 筐体の製作、設置

概要

筐体の製作を行い、設置・稼働開始した。



背景と目的

前回に引き続き、筐体を製作し、完成させる。



詳細

1. 仕様

設置環境がリモート水位センサと同様なので、要求されるものも同じである。


2. 材料

筐体には、リモート水位センサで実績のあるポリカーボネート製のボックス(ホームセンタービバホームで扱っている)のサイズ違いを使用。ボックス自体が透明なので、ボックス内にカメラレンズを構えても、ボックスを通して外が撮影可能だ。なので穴あけも必要ない。


3. 作製

基板と電池を以下のように詰めた。アンテナ長さ的にほぼぴったりだ。日除けは、段ボールとしてある。事前の検証で、段ボールは日除けとしてかなり効果的だとわかったため。見た目は悪いが、手軽な材料なので使わない手はない。

ソーラーパネルは、金具を使って、カメラレンズを設置する面に斜めに固定。ソーラーパネルがカメラレンズの上にくることで、ちょうどレンズ周辺の日除け+雨除けになるという狙い。とはいえ、横殴りの雨が来たらどうしようもないので、その時は拭くしかないが。


4. 設置、稼働

田植えが終わり、いよいよ稼働させる時が来た。以下のように、リモート水位センサ本体の下にぶら下げる形で、設置した。角度は、田んぼ全体がわかるように調整。


5. 動作確認

1週間ほど稼働させ、毎日送信されていることを確認。画質自体は、Unit-CAMのメモリの制約でSVGAとかなり低いのだが、とにかく成長記録としては役立つ。やはりレンズ前だけ穴あけしてアクリル板などにしたほうが、もう少しキレイにとれるのかもしれない。 あと、色味が悪いのは、後から多少の調整が効くのでよしとする。

というわけで、画質はともかくとして田んぼの真ん中からソーラーで電源を賄い、画像データを送信するシステムを稼働させることができた。課題はいろいろあるのだが、ひとまず形になったのでよかった。



まとめと今後の課題


田んぼ用リモートカメラ(3) ソフト実装

概要

田んぼ用リモートカメラのソフトウェアを実装した。



背景と目的

前回に引き続き、ソフトを実装する。



詳細

1. 基本仕様

1.1 動作の流れ

  1. 指定時刻に起動
  2. 初期設定
  3. 電池電圧計測
  4. 庫内温度計測
  5. 撮影
  6. 画像データ送信
  7. センサデータ送信
  8. スリープ

1.1.1 指定時刻に起動

このカメラは、リアルタイムクロックを積んでいないので自分で時刻を判断して起動することができない。そこで、

  • センサデータ送信時のレスポンスで、現在時刻をクラウドからもらう
  • 次の起動時刻までの時間を計算する
  • 起動までの残り時間分だけスリープする

という仕組みにする。(クラウド側から、起動までの残り時間を渡せば、起動時刻をクラウド指示できるがこのカメラの使用用途としては毎日同じ時間に起動すればよいので気にしない。過去に作成した家庭菜園の成長記録用カメラでは、起動までの残り時間を送っている。)

1.2 クラウド

  • 画像データ、センサデータとも、SORACOM Beamで、AWS APIGateway上に作成したエンドポイントに転送する
  • 画像データは、事前検証により送信はできるもののSORACOMからのレスポンスステータスコードが400になるが気にしない


2. ESP32

2.1 ツール

  • arduino-esp32 v1.0.6 (新しいバージョンだとうまく動かない部分があるため)

2.2 電池電圧計測

A/Dを読み取り、回路構成を考慮して変換するのみ。vbatは読み取り値。電圧の変化範囲が狭いので、4Vでの実測値1522LSBやゲイン32LSB/0.1Vを使う。

return (vbat - 1522.0) / 32.0 * 0.1 + 4.0;

2.3 温度計測

A/D入力を読み、S-8100Bの特性を考慮して電圧-温度変換をすればよいのではあるが、ESP32のA/D入力の非線形性やS-8100Bの絶対精度を考慮しながら、いろいろ悩んだ挙句、

  • 2点補正する手段が手元にない
  • 私の中での実績的に、A/Dゲイン(LSB/V)誤差がオフセット誤差に比べて大きいため、1点しか補正できない場合ゲイン誤差の補正のみを行うのが妥当

と考え、以下のようなオレオレ補正を入れ、ある程度信頼できる手元の温度計との合致性は向上したので良しとする。

adcは、A/D読み取り値だが、S-8100Bはある程度ノイズがあるため約30回の平均化をしている。

S-8100Bの絶対精度は、mTempOffsetで調整。

    // 電圧[V]
    // ESP32のADC特性(ATT=-11dB)
    // http://radiopench.blog96.fc2.com/blog-entry-1034.html
    //
    // 仕様: 3000LSB=2450mV@-11dB
    // 標準のADCオフセットは実績的に約-140[LSB]
    // 標準の傾きは仕様とオフセット実績を考慮すると3140LSB / 2.45V = 1281[LSB/V]
    // Vadc = (adc - (-140)) / 1281 * mGainAdjust
    Vadc = (double)(adc + 140.0) / 1281.0 * mAdcAttAdjust;

    // 温度[C]
    // S-8100Bの特性
    // +30degのとき1.508V(typ)
    // -8mV/K
    // T = -(V - 1.508) / 0.008 + 30
    temperature = -(Vadc - 1.508) / 0.008 + 30.0 + mTempOffset;

2.4 撮影

撮影は、家庭菜園の成長記録カメラとほぼ同様。解像度の設定だけSXGAに変更する。予備撮影など、オートホワイトバランス機能の癖を考慮した撮影手順が重要。

2.5 画像データ送信

BG96で送信するための基本的なコードは、こちらに従うため、ポイントだけ記載する。

画像データは、jpgファイルのバイナリデータをHTTPのボディに入れて送る。最終的に、AWS APIGatewayで受け取らせるため、Content-Typeヘッダをapplication/octet-streamにする。

ctx.printf("Content-Type: application/octet-stream\n");

また、カメラ個体を識別するIDのような文字列も一緒に送りたいのだが、ボディをバイナリデータにしてしまうと他の文字列データなどを入れにくい。そこで、ヘッダに以下のようなものを追加する。SORACOMの仕様では、受け取ったヘッダはそのまま流すので、AWS側で処理すればよい。

ctx.printf("machineId: %s\n", machineId);

2.6 センサデータ送信

センサデータは、JSONのボディで送信するので、こちらと同じ感じになるので省略。

レスポンスで、先述のスリープ時間を決めるための現在時刻を受け取る。JSONなので、こちらを参考に処理。

2.7 スリープ

リモート水位センサの場合と同様、ESP32のディープスリープを使用する。先述の通り、スリープ時間は、センサデータ送信のレスポンスで受け取った現在時刻からの次回起動目標時刻の差を計算して設定する。


3. クラウド

3.1 画像の受け取り

こちらのようにAPIGatewayでデータを受け取る。Lambdaでは、base64エンコードされた文字列として受け取るので、それをjpgバイナリに戻すようにbase64デコードして、処理してS3に保存する。

3.2 センサデータの受け取り

APIGatewayで、JSONを受け取り、Lambdaで処理してS3に保存する。


4. 動作確認

1週間ほど、テストを行い、毎日同じ時間に画像、センサデータがS3に蓄積されることを確認できた。



まとめと今後の課題

田んぼ用リモートカメラのソフト実装ができた。次は、筐体をしたい。


WinSCPで、勝手にリモートのファイルが消えてしまう

概要

WinSCPで、勝手にリモートのファイルが消えてしまうことがあるため、暫定の対策をした。原因は今のところ分かっていない。


状況

環境

  • Windows10 PC
  • WinSCP 5.19.6 (現象自体はこのバージョンよりも前から起きていると思われる)
  • 既定のエディタ: Visual Studio Code
  • LinuxシステムにSFTPで接続し、ファイルを編集

現象

  • WinSCP上でVisual Studio Codeを開いていると、いつの間にか当該ファイルがリモート側から削除されている
  • WinSCPには、ファイル削除時に確認ダイアログが出るオプションがあり、有効にしているにもかかわらず、確認なしに削除されている
  • VSCode上では、削除されたファイルは閉じられることなくそのまま表示されている。すぐにそのまま保存すれば問題ないだろうが、保存できず
  • 頻度としては、1日使っていると起こるかな?という感じ。神出鬼没なので、実験して原因を追い込もうにもなかなかうまくいかない。

WinSCPの環境設定


原因の推測

今のところ不明だが、気になる部分を列挙。

  • VSCodeとの組み合わせの問題か? → VSCode以外のエディタにしてみたらどうか、確認したい
  • なんとなく、WinSCPの画面リフレッシュのタイミングで消えているような感じがする。リフレッシュは、自分で行っていないので、WinSCPが自動的に何かのタイミングで行っている???

対策

今のところ原因がわからないが、このままでは怖くて使えないため、対策としてWinSCPのゴミ箱機能を使うことにした。 ファイルが削除されるときに、ゴミ箱に退避してくれるものだ。 各セッションの設定で、高度なセッションの設定画面を出し、環境>ゴミ箱から、任意の設定を行う。リモートの普段触ることがないディレクトリを指定しておく。

この状態で、問題の現象が起きファイルは削除されてしまったが、ゴミ箱に残ってくれた。なので、最悪の状況はどうにか回避できそう。

しかし、原因はつかみづらく、しばらく格闘が必要な気がする。 そもそも、VSCodeを使っている時点で、FTPツールは卒業すべきかもなあ。

自動水やり器の小型ポンプの分解

概要

自動水やり器 ver2に使用している小型ポンプの分解を行った。



背景と目的

自動水やり器 ver2で使用している小型ポンプの1つが壊れてしまった。別の個体に交換すると同時に、せっかくなので分解して中身がどうなっているか、調べてみる。



詳細

1. 対象

かつて、Amazonで買った

https://www.amazon.co.jp/gp/product/B07VNDZDCR/ref=ppx_yo_dt_b_search_asin_title?ie=UTF8&psc=1

に入っていたもの。配線が切れてしまった。


2. 分解する

まず、モーター側の水を吸い込む側のカバーを無理やり破壊して、水を掻く部分が見える状態にした。

次に、水掻きを取り外し、モーターの軸が出ている面を破壊したところ、モーターが見えた。切れた配線の隙間から侵入したのか、モーターの軸の出口とケースとの間から侵入したのかわからないが、多少浸水の跡がある。

取り出したモーターは、ホビー用のものと思われる。配線が切れただけだったので、電源をつなぐとモーターはちゃんと回った。


3. 故障に見える現象について

ネット上で、この手のホビー用小型ポンプについてすぐに壊れるとか、初期不良の個体が多いとか、いろいろ書き込みがあり信頼性が低いような印象を受ける。しかし、私の手元にある6個は、今回の配線が切れたこと以外のトラブルはなく、1年以上水の中で動作している。 私の推定なのだが、

初期不良や壊れたように見える現象は、モーター起動に必要な摩擦抵抗がたまたまその時大きく、動かなかっただけ

なのではないかと考えている。なぜなら、私が調べた限り、

  • 手持ちのポンプに同じ電圧を加え電流を測ってみると、個体ごとにかなり大きさがばらつく。100-200mAくらい。
  • 裸のモーターを無負荷で4Vを与えると、50mA程度。
  • 同じ個体でも、まれに動き出さないことがある。このとき、4V、400mA程度の大きな電流が流れる。モーター静止電流だろう。水かきを無理やり動かすとその勢いで回り始め、電流値も本来の大きさに落ち着く。
  • 構造上、モーター軸とケースとの間は浸水をできるだけ防ぎ、なおかつモーター軸が回るだけの非常にわずかな隙間を持たせる必要があることは明らかで、モーター軸、ケース寸法の個体差で摩擦抵抗の大きさはかなりばらつくだろう。

だから。初期不良を指摘している人は、いちいち電流など測っていないだろうから、何が起きているのか、わからないのだと思う。

というわけで、私が2年近く使用した結果としては、

小型で安価だが実際には不良品はほとんど存在しない。もともと動く部分のあるものだから多少のメンテナンスは必要であることを考慮すれば、十分に役に立つポンプだ。

と感じる。



まとめと今後の課題

2年近く使ったポンプについて、中身を分解することで理解が深まった。