4-20mA タイプ風速計

中国製 4-20mA タイプ風速計について調べて見た

4-20mAは、産業機器(センサや測定器)で広く標準採用されているアナログ電流信号の規格です。
測定値の0%を4mA、100%を20mAの範囲で伝送し、ノイズに強く長距離(数百m)でも信号が減衰
しないため、安定した監視・制御が可能です。

4-20mAの主な特徴・メリット

断線検知が可能: 0mAが「故障(断線)」、4mAが「測定値0(最小値)」を示すため、異常検知が容易。
ノイズに強い: 電流信号のため、電力設備周辺などノイズが多い環境でも安定した伝送が可能です。
長距離伝送が可能: 電圧信号と異なり、配線抵抗の影響を受けにくいため、遠くの機器まで正確に信
号を届けられる。
容易な変換: 抵抗器を通すことで、1~5Vの電圧入力に簡単に変換し、PLCやADコンバーターに取
り込める。

4-20mA 風速計 測定範囲0~30m / s 4~20mA
購入先 Ongwan社 アマゾン ¥5,509

この商品には説明書が添付されている、ただし数種類のタイプについて全部説明されているので実際
に買った物がどれに該当するかが分からない。
本体にも型番の記載が無いし、分解しても他の機種との違いが判別できない商品である。
実際動かしてみないとRS485なのか0-10Vなのか4-20mAなのか0-5Vタイプなのかが分からない。
製造元も不明、購入間違いなどのトラブルが予想される。
アマゾンの広告の文言もあやふやな表現が多いので間違いやすい、現に私は何度も間違えて注文した
被害者です。



設計方針

4線式なので茶色リードに12V 、黒色リードをGNDへ接続し、電流ラインの黄色 青に負荷抵抗250Ω
程度 を入れて両端をADコンバーターに入力して測定する形になる。

適当なADコンバーターを探してみたらスイッチサイエンスに「M5stack AIN4-20mAユニット(STM32G030)」というのを見つけた。

M5STACK-U162
購入先 スイッチサイエンス ¥1,386

特徴

・4-20 mA 測定 1チャンネル
・STM32G030F6 32 bit Cortex-M0+ CPU
・I2C通信
・2/4線センサ対応(ジャンパーキャップで切替)
・ガルバニック絶縁チップ搭載
・プログラミング・プラットフォーム : Arduino、UIFlowなど

仕様

・MCU : STM32G030F6(64 KBフラッシュ/8 KB SRAM)
・信号絶縁IC:HCNR200
・オペアンプ:SGM321YC5/TR
・I2Cアドレス:0x55
・IN+およびIN-入力インピーダンスの標準値 200Ω
・動作温度:0~40℃
・外部DC電源:9~24 V
・製品寸法 : 56 x 24 x 13 mm
・製品重量 : 8.2 g

回路図

通信プロトコル


回路図


電源は3種類必要になります。
5V動作の商品ですが、幸いI2Cラインは3.3VでESP-WROOM-02と直結出来ます。
プルアップ抵抗もM5stack AIN4-20mAユニット側に付いているので不要です。


テストスクリプト(電流[mA]を直接読む例)

#include <Wire.h>

#define AIN_I2C_ADDR 0x55
#define REG_CURRENT  0x20

void setup() {
  Serial.begin(115200);
  Wire.begin(4, 5);  // SDA, SCL
}

uint16_t readU16(uint8_t reg) {
  Wire.beginTransmission(AIN_I2C_ADDR);
  Wire.write(reg);
  Wire.endTransmission(false);  // Repeated START

  Wire.requestFrom(AIN_I2C_ADDR, (uint8_t)2);
  if (Wire.available() == 2) {
    uint8_t l = Wire.read();
    uint8_t h = Wire.read();
    return (h << 8) | l;
  }
  return 0;
}

void loop() {
  uint16_t rawCurrent = readU16(REG_CURRENT);
  float current_mA = rawCurrent / 100.0;

  Serial.print("Current = ");
  Serial.print(current_mA, 2);
  Serial.println(" mA");

  delay(1000);
}

Unit AIN4-20mA のI2C仕様

Raw ADC(12bit)
ADC1-L 0x00
ADC1-H 0x01
12bit ADC (0~4095)

電流値(mA換算済み)
Current-L 0x20
Current-H 0x21
STM32側でスケーリング済みなのでESP-WROOM-02側で計算は不要

キャリブレーション
Ref Current-L 0x30
Ref Current-H 0x31

注意点
・Low -> Highの順で読む
・数値はuint16_t (リトルエンディアン)
・電流はmA x 100形式




4-20mA 式風速計 完成スクリプト

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/

キャリブレーションの設定値は個々の機器でも異なる可能性があるので値は調整して下さい。
風速0.05 m/s以下はノイズと考えられますので削除しています。

#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <Wire.h>

#define AIN_I2C_ADDR 0x55
#define REG_CURRENT  0x20
#define REG_CAL      0x30

#define WIFI_SSID "your_ssid"
#define WIFI_PWD  "your_passwd"
const char* mqtt_server = "192.168.1.200";

#define LED 16

WiFiClient espClient;
PubSubClient client(espClient);

uint8_t count = 0;
unsigned long lastSample = 0;

// ===== I2C 16bit Read =====
uint16_t readU16(uint8_t reg) {
  Wire.beginTransmission(AIN_I2C_ADDR);
  Wire.write(reg);
  Wire.endTransmission(false);
  Wire.requestFrom(AIN_I2C_ADDR, (uint8_t)2);

  if (Wire.available() == 2) {
    uint8_t l = Wire.read();
    uint8_t h = Wire.read();
    return (h << 8) | l;
  }
  return 0;
}

// ===== I2C 16bit Write (Calibration) =====
void writeU16(uint8_t reg, uint16_t value) {
  Wire.beginTransmission(AIN_I2C_ADDR);
  Wire.write(reg);
  Wire.write(value & 0xFF);
  Wire.write(value >> 8);
  Wire.endTransmission();
}

// ===== MQTT reconnect =====
void reconnect() {
  while (!client.connected()) {
    String clientId = "ESP8266-";
    clientId += String(random(0xffff), HEX);
    client.connect(clientId.c_str());
  }
}

// ===== 電流 → 風速 =====
float readWindSpeed() {
  uint16_t raw = readU16(REG_CURRENT);
  float current = raw / 100.0;

  float wind = (current - 4.0) * 1.875;

  if (wind < 0) wind = 0;
  if (wind > 40) wind = 40;

  return wind;
}

void setup() {
  Serial.begin(115200);
  Wire.begin(4, 5);
  Wire.setClock(100000);

  WiFi.config(IPAddress(192,168,1,100),
              IPAddress(192,168,1,1),
              IPAddress(255,255,255,0));

  WiFi.begin(WIFI_SSID, WIFI_PWD);
  while (WiFi.status() != WL_CONNECTED) delay(500);

  client.setServer(mqtt_server, 1883);

  pinMode(LED, OUTPUT);

  // ===== キャリブレーション例 =====
  // 4.00mA基準なら
  writeU16(REG_CAL, 400);
}

void loop() {
  if (!client.connected()) reconnect();
  client.loop();

  if (millis() - lastSample >= 1000) {

    float wind = readWindSpeed();
    if (wind < 0.05) wind =0; //ノイズカット
    char msg[16];
    dtostrf(wind, 6, 2, msg);
    
        count +=1;
    if(count > 3){
          client.publish("wind_speed", msg);
          Serial.print("Wind: ");
          Serial.println(msg);
           count = 0;
    }
    
    digitalWrite(LED, !digitalRead(LED));
    lastSample = millis();
  }
}

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は今までと使い方が違うので慣れていない私には正直使いにくいと思います。

[{"id":"a38d57728fc711f6","type":"tab","label":"4-20mA 風速計","disabled":false,"info":"","env":[]},{"id":"mqtt_in","type":"mqtt in","z":"a38d57728fc711f6","name":"Wind Speed","topic":"wind_speed","qos":"2","datatype":"auto-detect","broker":"54ef697d8fc03043","nl":false,"rap":false,"inputs":0,"x":160,"y":140,"wires":[["cbd9067b5cb87090"]]},{"id":"cbd9067b5cb87090","type":"function","z":"a38d57728fc711f6","name":"単位付与","func":"var temp = msg.payload;\nvar data = msg.payload + \" m/s\";\nmsg.payload = data;\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":340,"y":140,"wires":[["ddbf977bb1ec7a9e"]]},{"id":"ddbf977bb1ec7a9e","type":"ui-text","z":"a38d57728fc711f6","group":"9e19fe58b00ac270","order":1,"width":0,"height":0,"name":"風速","label":"","format":"{{msg.payload}}","layout":"row-center","style":true,"font":"","fontSize":"38","color":"#ffffff","wrapText":false,"className":"","value":"payload","valueType":"msg","x":510,"y":140,"wires":[]},{"id":"54ef697d8fc03043","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":"9e19fe58b00ac270","type":"ui-group","name":"風速","page":"3c8c8ed8b7ba4bc5","width":6,"height":1,"order":1,"showTitle":true,"className":"","visible":"true","disabled":"false","groupType":"default"},{"id":"3c8c8ed8b7ba4bc5","type":"ui-page","name":"気象","ui":"3cd2cdb62b78a46e","path":"/page1","icon":"home","layout":"grid","theme":"502c1edb03e0863f","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":"3cd2cdb62b78a46e","type":"ui-base","name":"My Dashboard","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":false},{"id":"502c1edb03e0863f","type":"ui-theme","name":"my_theme","colors":{"surface":"#505050","primary":"#0094ce","bgPage":"#505050","groupBg":"#505050","groupOutline":"#cccccc"},"sizes":{"density":"default","pagePadding":"12px","groupGap":"12px","groupBorderRadius":"4px","widgetGap":"12px"}},{"id":"7bf6440b4a42d5b6","type":"global-config","env":[],"modules":{"@flowfuse/node-red-dashboard":"1.30.2"}}]
こんな感じになります




3秒平均・10分平均・最大瞬間 + JSON + エラー送信

更に機能を増やしたタイプ

送信JSON例

{
“wind_3s”: 2.35,
“wind_10m”: 1.82,
“max_10m”: 5.21,
“current”: 6.12,
“error”: 0
}

エラー定義
0 正常
1 4mA未満(断線)
2 20.5mA以上 (異常)
3 I2C読み取り失敗

#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <Wire.h>

#define AIN_I2C_ADDR 0x55
#define REG_CURRENT  0x20
#define REG_CAL      0x30

// ===== 風速仕様 =====
#define SAMPLE_INTERVAL 500
#define AVG3_COUNT 6
#define AVG10_COUNT 200

#define WIFI_SSID "your_ssid"
#define WIFI_PWD  "your_passwd"
const char* mqtt_server = "192.168.1.200";

#define LED 16

WiFiClient espClient;
PubSubClient client(espClient);

float buffer3[AVG3_COUNT];
float buffer10[AVG10_COUNT];

int idx3 = 0;
int idx10 = 0;
uint8_t count = 0;
float max10min = 0.0;
unsigned long lastSample = 0;

// ===== I2C =====
uint16_t readU16(uint8_t reg) {
  Wire.beginTransmission(AIN_I2C_ADDR);
  Wire.write(reg);
  if (Wire.endTransmission(false) != 0) return 0xFFFF;

  Wire.requestFrom(AIN_I2C_ADDR, (uint8_t)2);
  if (Wire.available() == 2) {
    uint8_t l = Wire.read();
    uint8_t h = Wire.read();
    return (h << 8) | l;
  }
  return 0xFFFF;
}

void writeU16(uint8_t reg, uint16_t value) {
  Wire.beginTransmission(AIN_I2C_ADDR);
  Wire.write(reg);
  Wire.write(value & 0xFF);
  Wire.write(value >> 8);
  Wire.endTransmission();
}

// ===== MQTT reconnect =====
void reconnect() {
  while (!client.connected()) {
    String clientId = "ESP8266-";
    clientId += String(random(0xffff), HEX);
    client.connect(clientId.c_str());
  }
}

// ===== 電流 → 風速 =====
int errorCode = 0;
float lastCurrent = 0;

float readWindSpeed() {
  uint16_t raw = readU16(REG_CURRENT);
  if (raw == 0xFFFF) {
    errorCode = 3;
    return 0;
  }

  float current = raw / 100.0;
  lastCurrent = current;

  if (current < 3.8) errorCode = 1;
  else if (current > 20.5) errorCode = 2;
  else errorCode = 0;

  float wind = (current - 4.0) * 1.875;

  if (wind < 0) wind = 0;
  if (wind > 30) wind = 30;

  return wind;
}

void setup() {
  Serial.begin(115200);
  Wire.begin(4, 5);
  Wire.setClock(100000);

  WiFi.config(IPAddress(192,168,1,100),
              IPAddress(192,168,1,1),
              IPAddress(255,255,255,0));

  WiFi.begin(WIFI_SSID, WIFI_PWD);
  while (WiFi.status() != WL_CONNECTED) delay(500);

  client.setServer(mqtt_server, 1883);

  pinMode(LED, OUTPUT);
  writeU16(0x30, 401);   //4mA->OFFSET
  //writeU16(0x31, );   //->OFFSET

  Serial.println("Wind System Start");
}

void loop() {
  if (!client.connected()) reconnect();
  client.loop();

  if (millis() - lastSample >= SAMPLE_INTERVAL) {

    float instant = readWindSpeed();

    // ===== 3秒平均 =====
    buffer3[idx3++] = instant;
    if (idx3 >= AVG3_COUNT) idx3 = 0;

    float avg3 = 0;
    for (int i = 0; i < AVG3_COUNT; i++) avg3 += buffer3[i];
    avg3 /= AVG3_COUNT;
    if (avg3 < 0.05) avg3 =0; //ノイズカット
    

    // ===== 10分平均 =====
    buffer10[idx10++] = avg3;
    if (idx10 >= AVG10_COUNT) {
      idx10 = 0;
      max10min = 0;
    }

    float avg10 = 0;
    for (int i = 0; i < AVG10_COUNT; i++) avg10 += buffer10[i];
    avg10 /= AVG10_COUNT;

    if (avg3 > max10min) max10min = avg3;

    // ===== JSON送信 =====
    char json[200];
    sprintf(json,
      "{\"wind_3s\":%.2f,\"wind_10m\":%.2f,\"max_10m\":%.2f,\"current\":%.2f,\"error\":%d}",
      avg3, avg10, max10min, lastCurrent, errorCode);
    count +=1;
    if(count > 3){
        client.publish("wind/json", json);
        Serial.println(json);
        count = 0;
    }

    digitalWrite(LED, !digitalRead(LED));
    lastSample = millis();
  }
}

キャリブレーション手順

ゼロ点調整

・風速計を 無風状態 にする
・電流を読む → 例 4.12mA
・本来は 4.00mA → ずれている
・以下をsetup()内に記述する
  writeU16(0x30, 400);  (私の場合400を書いたら3.99mAになったので401を書き込んだ)

中間点校正

・既知風速(例 10m/s)
・理論電流 → 9.33mA
・実測 → 9.10mA
・933 をsetup()内に記述する
 writeU16(0x31, 933);   //OFFSET




NODE-REDフロー


[{"id":"0d94d678df5b46f8","type":"tab","label":"4-20mA 風速計","disabled":false,"info":"","env":[]},{"id":"mqtt_in","type":"mqtt in","z":"0d94d678df5b46f8","name":"Wind JSON","topic":"wind/json","qos":"0","datatype":"auto","broker":"mqtt_broker","nl":false,"rap":false,"inputs":0,"x":150,"y":200,"wires":[["json_parse","fbd1a5a8bca2555d"]]},{"id":"json_parse","type":"json","z":"0d94d678df5b46f8","name":"JSON","property":"payload","action":"obj","pretty":false,"x":310,"y":200,"wires":[["59bd7d16feb62e07","8bdd6726e2c5e19d","2504ae213039413b","5754cb0b6ebeeb11","55815999d31d511d"]]},{"id":"679cfca5faa28f7b","type":"ui-text","z":"0d94d678df5b46f8","group":"a2862a7d5d758378","order":1,"width":0,"height":0,"name":"3秒平均風速","label":"3秒平均風速","format":"{{msg.payload}}","layout":"row-spread","style":true,"font":"Arial,Arial,Helvetica,sans-serif","fontSize":"24","color":"#fafafa","wrapText":false,"className":"","value":"payload","valueType":"msg","x":670,"y":140,"wires":[]},{"id":"47c4f8d243f86071","type":"ui-text","z":"0d94d678df5b46f8","group":"a2862a7d5d758378","order":2,"width":0,"height":0,"name":"10分平均風速","label":"10分平均風速","format":"{{msg.payload}}","layout":"row-spread","style":true,"font":"Arial,Arial,Helvetica,sans-serif","fontSize":"24","color":"#fafafa","wrapText":false,"className":"","value":"payload","valueType":"msg","x":680,"y":180,"wires":[]},{"id":"9ab3a52b683ed644","type":"ui-text","z":"0d94d678df5b46f8","group":"a2862a7d5d758378","order":3,"width":0,"height":0,"name":"10分最大風速","label":"10分最大風速","format":"{{msg.payload}}","layout":"row-spread","style":true,"font":"Arial,Arial,Helvetica,sans-serif","fontSize":"24","color":"#fafafa","wrapText":false,"className":"","value":"payload","valueType":"msg","x":680,"y":220,"wires":[]},{"id":"44370e613070653f","type":"ui-text","z":"0d94d678df5b46f8","group":"a2862a7d5d758378","order":4,"width":0,"height":0,"name":"電流 mA","label":"電流 mA","format":"{{msg.payload}}","layout":"row-spread","style":true,"font":"Arial,Arial,Helvetica,sans-serif","fontSize":"24","color":"#fafafa","wrapText":false,"className":"","value":"payload","valueType":"msg","x":660,"y":260,"wires":[]},{"id":"8c9eb4402103a535","type":"ui-text","z":"0d94d678df5b46f8","group":"a2862a7d5d758378","order":5,"width":0,"height":0,"name":"エラー","label":"エラー","format":"{{msg.payload}}","layout":"row-spread","style":true,"font":"Arial,Arial,Helvetica,sans-serif","fontSize":"24","color":"#fafafa","wrapText":false,"className":"","value":"payload","valueType":"msg","x":650,"y":300,"wires":[]},{"id":"fbd1a5a8bca2555d","type":"debug","z":"0d94d678df5b46f8","name":"debug 1","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":320,"y":360,"wires":[]},{"id":"59bd7d16feb62e07","type":"function","z":"0d94d678df5b46f8","name":"3秒平均","func":"var temp = msg.payload.wind_3s;\nvar data = temp + \" m/s\" ;\nmsg.payload = data;\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":500,"y":140,"wires":[["679cfca5faa28f7b","c34cb28cd10ef255"]]},{"id":"8bdd6726e2c5e19d","type":"function","z":"0d94d678df5b46f8","name":"10分平均","func":"var temp = msg.payload.wind_10m;\nvar data = temp + \" m/s\";\nmsg.payload = data;\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":500,"y":180,"wires":[["47c4f8d243f86071"]]},{"id":"2504ae213039413b","type":"function","z":"0d94d678df5b46f8","name":"10分間最大","func":"var temp = msg.payload.max_10m;\nvar data = temp + \" m/s\";\nmsg.payload = data;\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":510,"y":220,"wires":[["9ab3a52b683ed644"]]},{"id":"5754cb0b6ebeeb11","type":"function","z":"0d94d678df5b46f8","name":"電流値","func":"var temp = msg.payload.current;\nvar data = temp + \" mA\";\nmsg.payload = data;\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":490,"y":260,"wires":[["44370e613070653f"]]},{"id":"55815999d31d511d","type":"function","z":"0d94d678df5b46f8","name":"エラー","func":"var temp = msg.payload.error;\nvar data = temp;\nmsg.payload = data;\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":490,"y":300,"wires":[["8c9eb4402103a535"]]},{"id":"c34cb28cd10ef255","type":"debug","z":"0d94d678df5b46f8","name":"debug 2","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":780,"y":60,"wires":[]},{"id":"mqtt_broker","type":"mqtt-broker","name":"MQTT","broker":"192.168.2.242","port":"1884","clientid":"","autoConnect":true,"usetls":false,"protocolVersion":4,"keepalive":15,"cleansession":true,"autoUnsubscribe":true,"birthTopic":"","birthQos":"0","birthPayload":"","birthMsg":{},"closeTopic":"","closePayload":"","closeMsg":{},"willTopic":"","willQos":"0","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":"68c42c888cb51352","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":"68c42c888cb51352","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":"3c78252c1acd63c5","type":"global-config","env":[],"modules":{"@flowfuse/node-red-dashboard":"1.30.2"}}]
こんな感じになります





組み立て

未来工業 PVKボックス
PVK-BOK 購入先 アマゾン ¥703
蓋を底にして取付用のM4ねじ穴を4か所開ける
後ろ面にケーブル用の穴2箇所開ける 
前面にLED用の穴あける
完成品
ステンレススリムヘッド小ねじ M4×10 (10本入り) 購入先 アマゾン ¥576 (10本)
ステンレス鋼製 M4x0.7mm ギザギザ付きフランジ六角ロックナット 購入先 アマゾン ¥399 (12本)
トラスコ中山 コルゲートチューブ 内径7.4mm x10m 購入先 アマゾン ¥1,209
100 x 100 x1.6mm厚鉄板
購入先 横山テクノ 購入価格 ¥370
鉄板を裏面にM4 2cmx2本
皿ねじで固定する
ネオジム磁石 耐荷重8kg
直径20mm 購入先 アマゾン ¥ 1,699 (10個)
固定台側にネオジム磁石
x2個をM4ねじで固定する

未来工業のケースを使用します。
防水とは言ってないですが、特に問題は無いでしょう。
上部のねじ穴はパテで塞ぎます。
逆さにしたので底部をシリコンなどで塞ぎます。
風速計からのケーブルは、最短でカット接続します、引っ張りまわすとノイズが乗ります。
天板の風速計固定ねじはM4x10mm x4本 後部ケーブル穴は風速計用5mmφ 電源ケーブル用は
内径7.4mmのコルゲートチューブが入るサイズに。
前面LED穴は3mmφ

固定用に100x100x1.5mmの鉄板を風速計の裏面にM4皿ねじ2本で固定する。
固定台側にはネオジム磁石2個をM4ねじで固定する。

完成品と運用の様子