カテゴリー: 気象

  • 降雨検知システムを作る1

    降雨検知システムを作る1

    ■ Seeedstudio 工業用 光学式 雨量計 RG-15 雨量センサ

    SEEEDSTUDIO社製 RG-15
    購入先 RobotShop Inc. 購入価格 ¥17,985

    「RG-15」という外見は雨センサーらしからぬ風貌をしたセンサーである。
    https://wiki.seeedstudio.com/ja/Sensor/SenseCAP/Accessories/Industrial-Grade-Optical-Rain-Gauge-RG-15/
    少々お高いが、安定動作するならと5年位の使用期間を考えてアスザックの後釜のメイン機として導
    入した。
    外観がポリカーボネート製なので、恐らくは5年程度で外観が劣化して使えなくなるだろうとは覚悟
    しての買い物だった。(メーカーサイトでも寿命は7~10年程度という記載がある)
    使用してみて、なかなか良いのもではあるが、ある時から晴天の日の強い光が当たっている際に降雨
    が全くないのに雨をしかも連続して検知するようになった。
    これは困ったと思ったが、外国製なのでメーカーに相談出来る分けでもなく自分で何とかする必要が出てきた。
    もしかしたら、数年使ったので見た目には分からないがセンサーのカバーのポリカーボネート製レン
    ズに微少な傷でも付いたのか? 最初の頃は何事もなかったので設計ミスとは考え難い。
    RG-15は起動時に緑色のLEDでレンズの状況を知らせる機能を持っている、3回点滅で通常起動、4回
    では状態は悪化しているが何とか使用できる、5回で要交換を知らせてくれるがまだ3回点滅なので
    一応レンズは正常?という事らしい。

    そこで応急措置として、晴天のしかも光の強い日に必ずなるので、照度センサーを一緒に作動させ、
    ある程度の照度があるときの雨検知は無効とするようにプログラムした。
    とりあえずこれで一難は去ったが、同時に晴天の日の雨が検知出来なくなってしまった。
    捨てようかと思ったが、少し冷静になってもう少し様子を見たいと思い、分解してレンズ部を中性洗
    で丁寧に洗浄を行い、スクリプトにも異常値を検知してやり過ごす仕掛けを入れてみた。

    春先の先日やはり降雨が無い日にAccが検出されたことがあった、この時は新しく作った異常値
    フィルターに引っかかったのでスクリプトでこれを除去(無視)してシステム全体は難を逃れた。
    コマンドでRG-15の累積データーをクリアーにしてRG-15システムの再起動を行ったら異常値は消えた
    が、おかしいときは連続して検出されるので何だかちょっと不安、レンズを交換した方がいいかも知
    れない。

    想像するトラブルの原因
    ① 表面の劣化(汚れ、微細傷・コーティング劣化)
    ② 内部IR LEDの経年劣化
    ③ 受光素子の感度変動
    ④ 本体温度が急上昇

    センサー個々の部品劣化だとすると新品交換しか方法は残っていない、高いので再度購入は無理。
    最初の頃は正常だったので2年経った今は微妙なレンズの汚れ、劣化の可能性が一番高い、アスザッ
    クのセンサーも2年で故障したがこれも同じなのか?
    はっきり自己診断機能でレンズ不良やLED不良と出ればまだ安心なのだが、レンズは最悪海外経由
    でus$18程度で買えるようなので。
    https://store.hydreon.com/shop/rain-sensor/

    このセンサーはRS232C(正確にはTTLレベルRS232C通信)経由でコマンドを送信し、データーを受け
    取る仕組みである。
    おおまかな仕様は以下の様になる。

    センサーの概要
    光学式雨量計「RG-15」における「Acc」は、通信データ(RS-232シリアル通信)において「累積降雨量(Accumulation)」を意味するパラメータです。
    具体的には、以下の要素を指しています。
    Acc (Accumulation):
    前回のメッセージ出力から、今回のメッセージ出力までに検知された降雨の総量。
    Accの意味する内容:
    この値は、RG-15内部で光の散乱を用いて雨滴を検知し、その量(mmまたはインチ)を算出します。
    従来の「転倒ます型」のような可動部がないため、ゴミ詰まりに強く、メンテナンスフリー(またはメンテナンスの頻度が非常に少ない)な特徴を持っています。
    RG-15の主な特徴と関連用語
    Acc (Accumulation): 累積雨量。
    EventAcc: 現在の「降雨イベント」で検知された雨量(降雨が停止してから60分後にリセットされる)。
    TotalAcc: デバイスの累積合計雨量(コマンドでリセットするまで増加し続ける)。
    Rint (Rain Intensity): 1分間の雨量から計算された時間雨量(intensity)。
    光学式(Solid-state): 赤外線を使って雨滴を検知する方式。
    精度: 従来の機械式転倒ます雨量計と同等以上の性能を追求し、約10%以内のノミナル精度(公称精度)を持つ。
    RG-15は、可動部がないため、清掃が困難な場所や長期間メンテナンスができない環境での観測に適しています。

    テスト前の注意事項
    これは私の失敗から申し上げます。
    説明書を読まないで先入観で扱うと思わぬ失敗をしてしまいますので注意して下さい。
    今回は壊れはしませんでしたが、ギリギリ危なかったです。
    ・電源は5V~16Vです、しかしRS232Cラインは3,3V駆動ですので注意して下さい。
    ・RS232C端子はRG-15のRXをESP-ROOM-02のTXに接続、RG-15のTXはESP-WROOM-02のRX  
     に接続(たすきがけ)します。

    内蔵ディップスイッチの設定
    RG15の裏底にある4本のねじを外すと分解出来ます。
    取扱説明書に従ってディップスイッチを設定します。
    単位はmm方式で決まり->1 分解能は0.001 ->1 か0.01 -> 0 で設定します。
    (1,2ピンのみ設定 3,4ピンは使わない)
    結局1000(高感度) または1100(低感度) のどちらかの設定になります。

    下記はGoogle翻訳した説明書なので一部分が翻訳不要のコマンドも日本語になっていますので注意し
    て下さい。
    元データーは以下のurlよりpdfマニュアルを入手出来ます。
    https://rainsensors.com/wp-content/uploads/sites/3/2020/07/rg-15_instructions_sw_1.000.pdf
    その他の情報url
    https://rainsensors.com/2020/08/10/use-a-rg-15-with-a-standard-rs232-system/

    今回は導入編という事で使うコマンドとしては表中の「R」コマンドのみです。
    (続稿2でフルスペック上級用スクリプトの説明もあります)

    最初は、テストの為、WindowsPCとRS232Cで接続し、コマンドを送信してどうゆう返答が返って来
    るか実験した。

    DSD TECH SH-U09C USB – TTLシリアルアダプター+ FTDI FT232RLチップ
    購入先 アマゾン 購入価格 ¥2,399
    DTECH USB TTL シリアル 変換 ケーブル 3.3V
    1m FTDI チップセット
    購入先 アマゾン 購入価格 ¥1,999

    上記のように色々なタイプがありますが、FTDIチップ搭載ならどれでも良いですのでご用意ください。
    注意することは、GND ,TX , RX 以外の端子は使用しないという事です。
    +3.3VはPC内部より供給されていますので、VCC端子にはなにも接続しないでください。
    RG-15のRS232Cラインは3.3V駆動です、USBシリアルアダプターにある電圧設定は3.3Vにジャンパー
    ピンで設定して下さい。
    Windows用のFTDIドライバーが入っていない場合にはダウンロードしてインストールして下さい。
    https://ftdichip.com/drivers/

    テスト 結線図

    注意することは
    USBシリアルアダプターのTX -> RG-15のRS232C INにRX -> RG-15のRS232C OUTに接続します。

    通信ソフトはフリーソフトで色々ありますが、データーの関係が分かり難いので独自のPythonスクリ
    プトで読み込みました。
    WindowsPC上の「Jupyter Notebook」などで実行して下さい。
    尚、ドッカー上からは、RS232Cポートへアクセス出来ないので行わないでください。
    ポート番号は、デバイスの管理から自身のUSB-TTL I/Fの番号を確認して入力します。
    20秒間ループしますのでその間にセンサーに水をかけて正常に検知するか動作を確認します。
    センサーに水をかけるとセンサー内部の緑色のセンサーが激しく点灯するので検知されたかが分かり
    ます。

    !pip install pyserial
    import serial
    import time
    
    # ループの継続時間(秒)
    duration = 20
    # 開始時の時刻を取得
    start_time = time.time()
    
    def hexdump_with_index(data: bytes):
        print("\n--- RAW DATA (HEX + INDEX) ---")
        for i, b in enumerate(data):
            asc = chr(b) if 32 <= b <= 126 else '.'
            print(f"{i:03d}: 0x{b:02X}  '{asc}'")
        print("------------------------------")
    
    def main():
        port_num = input("使用するCOMポート番号を入力してください(例: 11): ").strip()
    
        if not port_num.isdigit():
            print("COMポート番号は数字で入力してください")
            return
    
        port_name = f"COM{port_num}"
    
        try:
            ser = serial.Serial(
                port=port_name,
                baudrate=9600,
                bytesize=serial.EIGHTBITS,
                parity=serial.PARITY_NONE,
                stopbits=serial.STOPBITS_ONE,
                timeout=1.0,              # 改行タイムアウト 1秒
                xonxoff=True              # XON/XOFF
            )
        except Exception as e:
            print(f"シリアルポートを開けません: {e}")
            return
    
        print(f"接続成功: {port_name}")
    
        try:
            while time.time() - start_time < duration:
                # 受信バッファクリア
                ser.reset_input_buffer()
    
                # コマンド送信(R + CRLF)
                cmd = b"R\r\n"
                print("\n送信:", cmd)
                ser.write(cmd)
    
                time.sleep(0.1)
    
                # 1行分受信(CRLFまで)
                data = ser.readline()
    
                if not data:
                    print("応答がありません")
                    return
    
                print("\n--- RECEIVED TEXT ---")
                try:
                    print(data.decode("ascii"))
                except UnicodeDecodeError:
                    print("ASCII変換不可")
    
                # HEX & インデックス表示
                hexdump_with_index(data)
    
        finally:
            ser.close()
            print("\nポートをクローズしました")
    
    if __name__ == "__main__":
        main()
    

    以下はこのスクリプトを実行した結果です。

    使用するCOMポート番号を入力してください(例: 11): 11
    接続成功: COM11
    
    送信: b'R\r\n'
    
    --- RECEIVED TEXT ---
    Acc  0.00 mm, EventAcc  0.00 mm, TotalAcc 115.38 mm, RInt  0.00 mmph
    
    
    --- RAW DATA (HEX + INDEX) ---
    000: 0x41  'A'
    001: 0x63  'c'
    002: 0x63  'c'
    003: 0x20  ' '
    004: 0x20  ' '
    005: 0x30  '0'
    006: 0x2E  '.'
    007: 0x30  '0'
    008: 0x30  '0'
    009: 0x20  ' '
    010: 0x6D  'm'
    011: 0x6D  'm'
    012: 0x2C  ','
    013: 0x20  ' '
    014: 0x45  'E'
    015: 0x76  'v'
    016: 0x65  'e'
    017: 0x6E  'n'
    018: 0x74  't'
    019: 0x41  'A'
    020: 0x63  'c'
    021: 0x63  'c'
    022: 0x20  ' '
    023: 0x20  ' '
    024: 0x30  '0'
    025: 0x2E  '.'
    026: 0x30  '0'
    027: 0x30  '0'
    028: 0x20  ' '
    029: 0x6D  'm'
    030: 0x6D  'm'
    031: 0x2C  ','
    032: 0x20  ' '
    033: 0x54  'T'
    034: 0x6F  'o'
    035: 0x74  't'
    036: 0x61  'a'
    037: 0x6C  'l'
    038: 0x41  'A'
    039: 0x63  'c'
    040: 0x63  'c'
    041: 0x20  ' '
    042: 0x31  '1'
    043: 0x31  '1'
    044: 0x35  '5'
    045: 0x2E  '.'
    046: 0x33  '3'
    047: 0x38  '8'
    048: 0x20  ' '
    049: 0x6D  'm'
    050: 0x6D  'm'
    051: 0x2C  ','
    052: 0x20  ' '
    053: 0x52  'R'
    054: 0x49  'I'
    055: 0x6E  'n'
    056: 0x74  't'
    057: 0x20  ' '
    058: 0x20  ' '
    059: 0x30  '0'
    060: 0x2E  '.'
    061: 0x30  '0'
    062: 0x30  '0'
    063: 0x20  ' '
    064: 0x6D  'm'
    065: 0x6D  'm'
    066: 0x70  'p'
    067: 0x68  'h'
    068: 0x0D  '.'
    069: 0x0A  '.'
    ------------------------------
    
    ポートをクローズしました

    結果を見ると「R」コマンドを送信すると返送データーの先頭から05H~08HまでにAccの値が
    24H~27Hに「EventAcc」の値が格納されている事が分かります。
    ここをESP-WROOM-02を使って読めば必要な雨データーが得られる分けですね。
    以上が分かったところで完成したスクリプトは以下の様になります。

    RG-15 スクリプト ESP-WROOM-02用

    スクリプトの説明
    SoftWareシリアルポート4,5を使います。
    5秒間隔程度で「R」コマンドを使って雨データーを読出し、MQTT通信で送信しています。
    JSONでセンサー側から送信 topic -> rain_sensor/rg15/data


    取得項目:
    Acc
    EventAcc
    TotalAcc
    RInt
    LensBad(あれば):レンズの状態劣化の告知
    EmSat(あれば) :センサー内部赤外線LEDの劣化告知
    spike_filtered 

    mode:opeは観測モードで動作している事を示します。(Operation Modeの略)
    雨が降っているかどうかの確認のみなら「Acc」の状態で確認出来ます。
    EventAccやRintを観測すると雨の勢いや累積降雨量が分かります。
    LensBadはレンズが劣化した時にメーセージが出ます。
    Emsatは内蔵赤外線LEDの異常があれば表示します。
    spike_filteredは観測値の異常を検出した時に表示されます。
    ・ 1秒で0.2mm以上増加 → 異常
    ・EventAccだけ増えている場合→ 異常
    ・EmSat(内蔵赤外線LEDの異常)発生時は無効
    spike_filteres信号以外は5秒遅らせて伝搬させているので異常値が発生した場合に、検知
    がふらつくのを防止しています、検知されたら受信データーは破棄されます。
    異常値が頻発に発生する場合には、RG-15に何らかの問題が発生している可能性が高いです。
    リセットや蓄積データーをクリアすれば、一旦回復する場合もあるようですが完全な解決
    方法ではありません。

    const char* mqtt_server = “192.168.1.200”;では自身のMQTT Serverのアドレスを入れて下さい。
    WiFi.config(IPAddress(192, 168, 1, 100), IPAddress(192, 168, 1, 1), IPAddress(255, 255, 255, 0));
    でこのセンサーシステム自身のIPアドレスを指定しています。
    自身の環境に合わせて指定してください。
    自分の家の空いているIPアドレスを調べるには以下のソフト「Advanced IP Scanner」を使用すると
    便利。
    https://forest.watch.impress.co.jp/library/software/advipscanner/

    #include <ESP8266WiFi.h>
    #include <PubSubClient.h>
    #include <SoftwareSerial.h>
    
    #define WIFI_SSID "your_ssid"
    #define WIFI_PWD  "your_passwd"
    
    const char* mqtt_server = "192.168.1.200";
    
    SoftwareSerial mySerial(4, 5);   // RX, TX
    
    #define LED1 14
    #define LED2 16
    
    WiFiClient espClient;
    PubSubClient client(espClient);
    
    unsigned long lastSend = 0;
    float prevAcc = 0;
    float filteredAcc = 0;
    
    // ================= WiFi =================
    void connectWiFi() {
      WiFi.begin(WIFI_SSID, WIFI_PWD);
      while (WiFi.status() != WL_CONNECTED) {
        delay(500);
      }
    }
    
    // ================= MQTT Callback(未使用・将来拡張用) =================
    void callback(char* topic, byte* payload, unsigned int length) {
      // 現在は未使用
    }
    
    // ================= MQTT Reconnect =================
    void reconnect() {
    
      while (!client.connected()) {
    
        String clientId = "RG15-";
        clientId += String(random(0xffff), HEX);
    
        if (!client.connect(clientId.c_str())) {
          delay(2000);
        }
      }
    }
    
    // ================= RG15 R読取 =================
    void readRG15() {
    
      mySerial.print("R\r\n");
      delay(200);
    
      String data = mySerial.readStringUntil('\n');
      data.trim();
    
      if (data.length() == 0) return;
    
      float acc = 0;
      float eventAcc = 0;
      float totalAcc = 0;
      float rint = 0;
    
      int idx;
    
      idx = data.indexOf("Acc");
      if (idx >= 0)
        acc = data.substring(idx + 4).toFloat();
    
      idx = data.indexOf("EventAcc");
      if (idx >= 0)
        eventAcc = data.substring(idx + 9).toFloat();
    
      idx = data.indexOf("TotalAcc");
      if (idx >= 0)
        totalAcc = data.substring(idx + 9).toFloat();
    
      idx = data.indexOf("RInt");
      if (idx >= 0)
        rint = data.substring(idx + 5).toFloat();
    
      bool lensBad = data.indexOf("LensBad") >= 0;
      bool emSat   = data.indexOf("EmSat") >= 0;
    
      // ===============================
      // 異常値フィルタ(夏対策)
      // ===============================
    
      float diff = acc - prevAcc;
      bool spike = false;
    
      // 1秒で0.2mm以上増加 → 異常
      if (diff > 0.2) {
        spike = true;
      }
    
      // EventAccだけ増えている場合
      if (diff <= 0 && eventAcc > 0.1) {
        spike = true;
      }
    
      // EmSat発生時は無効
      if (emSat) {
        spike = true;
      }
    
      if (spike) {
        acc = prevAcc;
      } else {
        prevAcc = acc;
      }
    
      filteredAcc = acc;
    
      digitalWrite(LED1, filteredAcc > 0);
    
      char payload[256];
    
      snprintf(payload, sizeof(payload),
        "{\"mode\":\"ope\","
        "\"acc\":%.2f,"
        "\"event_acc\":%.2f,"
        "\"total_acc\":%.2f,"
        "\"rint\":%.2f,"
        "\"lens_bad\":%s,"
        "\"emsat\":%s,"
        "\"spike_filtered\":%s}",
        filteredAcc, eventAcc, totalAcc, rint,
        lensBad ? "true" : "false",
        emSat   ? "true" : "false",
        spike   ? "true" : "false");
    
      client.publish("rain_sensor/rg15/data", payload);
    }
    
    // ================= SETUP =================
    void setup() {
    
      pinMode(LED1, OUTPUT);
      pinMode(LED2, OUTPUT);
    
      digitalWrite(LED1, LOW);
      digitalWrite(LED2, LOW);
    
      mySerial.begin(9600);
    
      WiFi.config(IPAddress(192,168,1,100),
                  IPAddress(192,168,1,1),
                  IPAddress(255,255,255,0));
    
      connectWiFi();
    
      client.setServer(mqtt_server, 1883);
      client.setCallback(callback);
    
      // Polling固定
      mySerial.print("P\r\n");
    }
    
    // ================= LOOP =================
    void loop() {
    
      if (!client.connected())
        reconnect();
    
      client.loop();
    
      if (millis() - lastSend > 1000) {
        lastSend = millis();
        readRG15();
      }
    
      // 動作確認LED
      static unsigned long ledTimer = 0;
      if (millis() - ledTimer > 500) {
        ledTimer = millis();
        digitalWrite(LED2, !digitalRead(LED2));
      }
    }

    回路図

    上記例では5pinのコネクタを使用している

    組み込み実装

    タカチ電機工業 SW-120B
    購入先 アマゾン 購入価格 ¥575+送料¥250
    航空コネクタ4P オス/メス パネル
    購入先 アマゾン 購入価格 ¥1,399(6個)
    防水キャップ アンテナパーツ
    アマゾン 10個入り 購入先 アマゾン
    購入価格 ¥1,640
    取り付け金具を自作する
    あればカッテイングツールで防水シールを作って
    収納ボックスに貼る。
    取付金具をセットする
    こんな感じに仕上がる

    運用の様子



    NODE-RED上の作業

    NODE-RED ダッシュボード

    雨センサーで計測したデーターは、MQTT送信されます。
    NODE-RED 上に設けたMQTT ブローカー経由で受信し、ダッシュボード上に常時表示します。
    まずは、ダッシュボードに表示する手順をご説明します。

    事前にRaspberry Pi4やPi5にNODE-RED とMQTTブローカーを下記の記事を参考に設定して準備します。
    https://marginalvillage.com/page/4/

    NODE-REDで右上にある三のアイコンをクリックして「パレットの管理」を開き
    「ノードを追加」で「@flowfuse/node-red-dashboard」を追加して下さい。
    ちょっと前までは通常「node-red-dashboard」を使っていましたが、最近不具合が発生しております。
    具体的には、私はダッシュボードが開かなる現象を経験しました。
    正常動作する場合も多いので厄介です。
    現在「非推奨」マークが付いています。
    新しいダシュボード2は今までと使い方が違うので慣れていない私には正直使いにくいと思います。

    雨以外の天気の「曇り」、「晴れ」、「快晴」の情報も無いと意味をなさ無いのでこのブログ内にある
    「AI空模様告知システム」を作成運用してからになります。
    https://marginalvillage.com/page/2/

    まず、空模様のアイコンを準備します。
    自分で作成するか、フリーの素材を活用させてもらうか、最近はAIソフトが作成してくれるとい
    う選択肢もあるようです。

    快晴
    晴れ
    曇天

    自作する場合には以下のような無料サイトを利用すると良いでしょう。
    https://www.photopea.com/l/ja_jp/
    https://forest.watch.impress.co.jp/library/software/inkscape/
    https://www.adobe.com/jp/express/

    用意した天気の画像を入れるフォルダーは決まり事で通常 /home/pi/Public になります。
    2026/3時点のRaspberry PI OS (other)->Legasy,64bit OS Bookwormではこのフォルダが漢字「公開」
    になっている場合があります。(最新のDebian Trixleでは検証していません)
    その場合には、「Public」直してください、そのままだと使用不可です。
    このフォルダー内に画像を作って入れておくわけですが、デフォルトではその機能が無効に
    なっていますのでまずは機能を有効にします。

    Publicフォルダーを有効にする手順
    NODE-REDサーバーを動かしているRaspberry Piの.「.node-red」フォルダーの中に「setting.js」という
    のがあります。
    このファイルをMouspadで開き適当なところへ追加で書き加えます。
    尚、「.node-red」は隠しファイルなので見つからない場合には、「Ctrl-H」を押して表示させて下さい。
    「setting.js」に「httpStatic: ‘/home/pi/Public/’,」を加筆する。

        /**  
         * All static routes will be appended to httpStaticRoot
         * e.g. if httpStatic = "/home/nol/docs" and  httpStaticRoot = "/static/"
         *      then "/home/nol/docs" will be served at "/static/"
         * e.g. if httpStatic = [{path: '/home/nol/pics/', root: "/img/"}]
         *      and httpStaticRoot = "/static/"
         *      then "/home/nol/pics/" will be served at "/static/img/"
         */
        //httpStaticRoot: '/static/',
        httpStatic: '/home/pi/Public/',

    最後にある「,」を忘れないようにして下さい、無いとNODE-REDが起動しません。
    加筆したら保存してからNODE-REDを再起動させます。
    画像は一旦「Weather」フォルダーを作成してその中に入れます

    雨センサー以外にお天気センサーも組み込みが必要にになりますので、ここでは原理的な解説
    に留めます。
    このサンプルでは、雨が止んでも他のセンサーからの情報が入らないので一旦雨を検知すると
    雨のまま更新されませんので注意して下さい。
    デバッグ用の天気ボタンをクリックすれば、ダッシュボード上に天気アイコンが表示されます。

    こんな感じになります

    降雨センサーRG-15 NODE-RED フロー

    [{"id":"882d06e591bc360b","type":"tab","label":"降雨センサーRG-15","disabled":false,"info":"","env":[]},{"id":"efa7b8f9a7fb74d2","type":"mqtt in","z":"882d06e591bc360b","name":"","topic":"rain_sensor/rg15/data","qos":"2","datatype":"auto-detect","broker":"ec7e282464ec4f73","nl":false,"rap":true,"rh":0,"inputs":0,"x":180,"y":560,"wires":[["1a15413cc4403b86","55bb0591c76c611d"]]},{"id":"19b55c9171967a67","type":"inject","z":"882d06e591bc360b","name":"雨->0","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"0","payloadType":"str","x":270,"y":220,"wires":[["8fa3ae9b5a7a2613"]]},{"id":"59c92712031913d3","type":"inject","z":"882d06e591bc360b","name":"快晴->1","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"1","payloadType":"str","x":270,"y":260,"wires":[["8fa3ae9b5a7a2613"]]},{"id":"846c8d3d0c8a24eb","type":"inject","z":"882d06e591bc360b","name":"晴れ->2","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"2","payloadType":"str","x":270,"y":300,"wires":[["8fa3ae9b5a7a2613"]]},{"id":"867d49a31f07b554","type":"inject","z":"882d06e591bc360b","name":"曇天->3","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"3","payloadType":"str","x":270,"y":340,"wires":[["8fa3ae9b5a7a2613"]]},{"id":"aa8cafc754880fa9","type":"inject","z":"882d06e591bc360b","name":"雪->4","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"4","payloadType":"str","x":270,"y":380,"wires":[["8fa3ae9b5a7a2613"]]},{"id":"55bb0591c76c611d","type":"json","z":"882d06e591bc360b","name":"","property":"payload","action":"obj","pretty":false,"x":330,"y":460,"wires":[["e1eec65695fdacb2","c531c77ab3066cbf"]]},{"id":"1a15413cc4403b86","type":"debug","z":"882d06e591bc360b","name":"debug 11","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":380,"y":560,"wires":[]},{"id":"8fa3ae9b5a7a2613","type":"function","z":"882d06e591bc360b","name":"天候画像変換","func":"var icon;\nvar weather = msg.payload;\nswitch(weather){\n    case \"0\":\n        icon = \"/Weather/rain.png\";\n        break;\n    case \"1\":\n        icon = \"/Weather/sunny.png\";\n        break; \n    case \"2\":\n        icon = \"/Weather/sunny_cloudy.png\";\n        break; \n    case \"3\":\n        icon = \"/Weather/cloudy.png\";\n        break;\n    case \"4\":\n        icon = \"/Weather/snow.png\";\n        break;\n    case \"5\":\n        var night = global.get(\"sun_status\");\n        switch(night){\n            case \"0\":\n                 icon = \"/Weather/rain.png\";\n                 break;\n            case \"1\":\n                 icon = \"/Weather/sunny.png\";\n                 break; \n            case \"2\":\n                 icon = \"/Weather/sunny_cloudy.png\";\n                 break; \n            case \"3\":\n                 icon = \"/Weather/cloudy.png\";\n                 break;\n            case \"4\":\n                 icon = \"/Weather/snow.png\";\n                 break;\n            default :\n                 icon = \"/Weather/cloudy.png\";\n        }\n}\nmsg.payload = icon;\nreturn msg","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":480,"y":220,"wires":[["ef043d9cb4a88012"]]},{"id":"cdee9cbb28ddaa59","type":"function","z":"882d06e591bc360b","name":"雨以外は通過","func":"var status =global.get(\"rain_status\");\nif (status == \"0\"){\n     var temp = String(msg.payload);\n     global.set(\"sun_status\",temp);\n     msg.payload = temp;\n     return msg;\n}\n\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":440,"y":140,"wires":[["8fa3ae9b5a7a2613"]]},{"id":"af020ffa9c4bf041","type":"mqtt in","z":"882d06e591bc360b","name":"","topic":"sky_pat","qos":"2","datatype":"auto-detect","broker":"ec7e282464ec4f73","nl":false,"rap":true,"rh":0,"inputs":0,"x":270,"y":140,"wires":[["cdee9cbb28ddaa59"]]},{"id":"ef043d9cb4a88012","type":"ui-template","z":"882d06e591bc360b","group":"a2862a7d5d758378","page":"","ui":"","name":"今の天気","order":1,"width":0,"height":0,"head":"","format":"<template>\n    <div style=\"text-align:center\">\n        <img\n      :src=\"msg.payload\"\n      alt=\"weather_icon\"\n      width=\"92\"\n      height=\"92\"\n    />\n    </div>\n</template>","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":680,"y":220,"wires":[[]]},{"id":"833783cce9bc1e1b","type":"ui-gauge","z":"882d06e591bc360b","name":"検知された雨量mm","group":"a2862a7d5d758378","order":2,"value":"payload","valueType":"msg","width":"2","height":"2","gtype":"gauge-half","gstyle":"needle","title":"1時間累積雨量mm","alwaysShowTitle":false,"floatingTitlePosition":"top-left","units":"","icon":"","prefix":"","suffix":"","segments":[{"from":"0","color":"#b8d987","text":"","textType":"label"},{"from":"20","color":"#9ac6a9","text":"","textType":"label"},{"from":"30","color":"#fee690","text":"","textType":"label"},{"from":"50","color":"#f38686","text":"","textType":"label"},{"from":"100","color":"#f90606","text":"","textType":"label"}],"min":0,"max":"100","sizeThickness":16,"sizeGap":4,"sizeKeyThickness":8,"styleRounded":true,"styleGlow":false,"className":"","x":850,"y":500,"wires":[[]]},{"id":"1248484e8a44aae7","type":"comment","z":"882d06e591bc360b","name":"デバッグ用","info":"","x":120,"y":300,"wires":[]},{"id":"786998221f88076e","type":"comment","z":"882d06e591bc360b","name":"1mm〜5mm未満: シトシトと降る。傘が必要。","info":"","x":980,"y":180,"wires":[]},{"id":"5566bebe0fbd4069","type":"comment","z":"882d06e591bc360b","name":"10mm〜20mm未満: ザーザーと降る。地面からの跳ね返りで足元が濡れる。","info":"","x":1070,"y":220,"wires":[]},{"id":"10dee76293678985","type":"comment","z":"882d06e591bc360b","name":"20mm〜30mm未満: どしゃ降り。ワイパーを速くしても見づらい。","info":"","x":1040,"y":260,"wires":[]},{"id":"dc84100ffd8685d4","type":"comment","z":"882d06e591bc360b","name":"30mm〜50mm未満: 激しい雨。バケツをひっくり返したよう。道路が川のようになる。","info":"","x":1100,"y":300,"wires":[]},{"id":"9adc0767658193bd","type":"comment","z":"882d06e591bc360b","name":"50mm〜80mm未満: 非常に激しい雨。滝のように降る。車は運転危険、地下空間は危険。","info":"","x":1110,"y":340,"wires":[]},{"id":"3c907ac53515edc6","type":"comment","z":"882d06e591bc360b","name":"80mm以上: 猛烈な雨。息苦しく、恐怖を感じる。土砂災害や浸水の危険。","info":"","x":1060,"y":380,"wires":[]},{"id":"6d83484134a7c2c4","type":"comment","z":"882d06e591bc360b","name":"1時間の降雨量 EventACC ","info":"","x":920,"y":120,"wires":[]},{"id":"4d5226877986c05f","type":"function","z":"882d06e591bc360b","name":"rain = \"0\"","func":"var spike = global.get(\"spike\");\nif(!spike){\nvar temp = parseFloat(msg.payload.acc);\nvar ame = \"\";\nif (temp > 0.0){\n    ame = \"0\";\n    global.set(\"rain_status\",\"1\");\n}else{\n    ame = \"5\";\n    global.set(\"rain_status\",\"0\");\n}\nmsg.payload = ame;\nreturn msg;\n}else{\nglobal.set(\"rain_status\",\"0\");\n}","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":640,"y":460,"wires":[["8fa3ae9b5a7a2613"]]},{"id":"5a7ec78ad55b19bb","type":"function","z":"882d06e591bc360b","name":"event_acc","func":"var spike = global.get(\"spike\");\nif (!spike){\nvar temp = msg.payload.event_acc;\nmsg.payload = temp;\nreturn msg;\n}","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":650,"y":500,"wires":[["833783cce9bc1e1b"]]},{"id":"c531c77ab3066cbf","type":"function","z":"882d06e591bc360b","name":"spike","func":"var temp = msg.payload.spike_filtered;\nglobal.set(\"spike\",temp);\nmsg.payload = temp;\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":630,"y":540,"wires":[["d3bc82ebbbd7dcd1"]]},{"id":"d3bc82ebbbd7dcd1","type":"function","z":"882d06e591bc360b","name":"異常値?","func":"var spike = msg.payload;\nvar data =\"\";\nif (spike){\n    data = \"異常値検知\";\n}else{\n    data = \"正常\";\n}\nmsg.payload = data;\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":760,"y":540,"wires":[["90fb6e5f46f778b5"]]},{"id":"90fb6e5f46f778b5","type":"ui-text","z":"882d06e591bc360b","group":"a2862a7d5d758378","order":3,"width":6,"height":0,"name":"警告","label":"","format":"{{msg.payload}}","layout":"row-spread","style":true,"font":"","fontSize":16,"color":"#fafafa","wrapText":false,"className":"","value":"payload","valueType":"msg","x":890,"y":540,"wires":[]},{"id":"e1eec65695fdacb2","type":"delay","z":"882d06e591bc360b","name":"","pauseType":"delay","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":480,"y":460,"wires":[["5a7ec78ad55b19bb","4d5226877986c05f"]]},{"id":"ec7e282464ec4f73","type":"mqtt-broker","name":"","broker":"192.168.1.200","port":"1883","clientid":"","autoConnect":true,"usetls":false,"protocolVersion":"4","keepalive":"60","cleansession":true,"autoUnsubscribe":true,"birthTopic":"","birthQos":"0","birthRetain":"false","birthPayload":"","birthMsg":{},"closeTopic":"","closeQos":"0","closeRetain":"false","closePayload":"","closeMsg":{},"willTopic":"","willQos":"0","willRetain":"false","willPayload":"","willMsg":{},"userProps":"","sessionExpiry":""},{"id":"a2862a7d5d758378","type":"ui-group","name":"今日の天気","page":"cef20d4917f1c9a7","width":6,"height":1,"order":1,"showTitle":false,"className":"","visible":"true","disabled":"false","groupType":"default"},{"id":"cef20d4917f1c9a7","type":"ui-page","name":"気象","ui":"debf650e7f1343d3","path":"/page1","icon":"home","layout":"grid","theme":"a45c7f2adf09be21","breakpoints":[{"name":"Default","px":"0","cols":"3"},{"name":"Tablet","px":"576","cols":"6"},{"name":"Small Desktop","px":"768","cols":"9"},{"name":"Desktop","px":"1024","cols":"12"}],"order":1,"className":"","visible":"true","disabled":"false"},{"id":"debf650e7f1343d3","type":"ui-base","name":"画面名","path":"/dashboard","appIcon":"","includeClientData":true,"acceptsClientConfig":["ui-notification","ui-control"],"showPathInSidebar":false,"headerContent":"page","navigationStyle":"default","titleBarStyle":"default","showReconnectNotification":true,"notificationDisplayTime":1,"showDisconnectNotification":true,"allowInstall":true},{"id":"a45c7f2adf09be21","type":"ui-theme","name":"気象","colors":{"surface":"#505050","primary":"#505050","bgPage":"#505050","groupBg":"#5f5f5f","groupOutline":"#cccccc"},"sizes":{"density":"default","pagePadding":"12px","groupGap":"12px","groupBorderRadius":"4px","widgetGap":"12px"}},{"id":"f8115d4185ef653a","type":"global-config","env":[],"modules":{"@flowfuse/node-red-dashboard":"1.30.2"}}]