0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【PoC】Track-Bay:Node-RED + MQTTでトラックバース予約システムを作ってみた

Last updated at Posted at 2025-09-19

【PoC】Track-Bay:Node-RED + MQTTでトラックバース予約を最短構築(標準ノードのみ)

対象

  • スマホ→予約→Dashboard反映までを最短で体験したい
  • 追加ノードなし(標準の node-red-dashboard のみ)で動かしたい

ゴール

  • スマホ(ブラウザ)から予約データを送信
  • Node-RED Dashboard に予約一覧を表示
  • MQTT(Mosquitto)でメッセージを確認

前提

  • Node-RED が動作(http://localhost:1880 を開ける)
  • Dashboardノードnode-red-dashboard)がインストール済み
    • ※ 未導入なら:メニュー → パレットの管理 → “ノードを追加” → node-red-dashboard

セットアップ(Ubuntuの例)

1) Node.js & Node-RED

bash
bash <(curl -sL https://raw.githubusercontent.com/node-red/linux-installers/master/deb/update-nodejs-and-nodered)
node-red-start

2) MQTTブローカ(Mosquitto)

bash
sudo apt update
sudo apt install -y mosquitto mosquitto-clients
sudo systemctl enable --now mosquitto

Windowsの場合:Node-RED / Mosquitto を各公式からインストール。以降の手順は同様。


使い方:フローをインポート

  • Node-RED エディタ → メニュー → インポート → 下のJSONを貼り付け → ImportDeploy
  • Dashboard を開く:http://<ホスト>:1880/ui
json
[
  { "id": "flow-trackbay", "type": "tab", "label": "Track-Bay Flow", "disabled": false, "info": "" },

  { "id": "ui-base", "type": "ui_base",
    "theme": { "name": "theme-light",
      "lightTheme": { "default": "#0094CE", "baseColor": "#ffffff", "baseFont": "Helvetica Neue", "edited": true, "reset": false },
      "darkTheme": { "default": "#097479", "baseColor": "#111111", "baseFont": "Helvetica Neue", "edited": true, "reset": false } },
    "site": { "name": "Track-Bay", "hideToolbar": "false", "allowSwipe": "true", "lockMenu": "false", "allowTempTheme": "true", "dateFormat": "YYYY-MM-DD",
      "sizes": { "sx": 48, "sy": 48, "gx": 6, "gy": 6, "cx": 6, "cy": 6, "px": 0, "py": 0 } } },

  { "id": "tab-trackbay", "type": "ui_tab", "z": "", "name": "Track-Bay", "icon": "dashboard", "order": 1 },
  { "id": "grp-reserve", "type": "ui_group", "z": "", "name": "予約フォーム", "tab": "tab-trackbay", "order": 1, "disp": true, "width": "6", "collapse": false },
  { "id": "grp-list", "type": "ui_group", "z": "", "name": "予約一覧", "tab": "tab-trackbay", "order": 2, "disp": true, "width": "6", "collapse": false },

  { "id": "mqtt-local", "type": "mqtt-broker", "name": "Local Mosquitto", "broker": "127.0.0.1", "port": "1883",
    "clientid": "", "usetls": false, "protocolVersion": "4", "keepalive": "60", "cleansession": true,
    "birthTopic": "", "birthQos": "0", "birthPayload": "", "closeTopic": "", "closePayload": "",
    "willTopic": "", "willQos": "0", "willPayload": "" },

  { "id": "ui-form", "type": "ui_form", "z": "flow-trackbay", "name": "予約入力", "label": "Track-Bay 予約",
    "group": "grp-reserve", "order": 0, "width": 0, "height": 0,
    "options": [
      { "label": "バース番号 (1-20)", "value": "bay", "type": "number", "required": true },
      { "label": "開始 (HH:MM)", "value": "from", "type": "text", "required": true },
      { "label": "終了 (HH:MM)", "value": "to", "type": "text", "required": true },
      { "label": "担当者名", "value": "user", "type": "text", "required": true }
    ],
    "payload": "", "submit": "予約する", "cancel": "クリア", "topic": "",
    "x": 200, "y": 140, "wires": [["fn-build"]] },

  { "id": "fn-build", "type": "function", "z": "flow-trackbay", "name": "予約メッセージ整形",
    "func": "const p = msg.payload || {};\nconst now = Date.now();\nmsg.topic = \"trackbay/reserve\";\nmsg.payload = {\n  bay: Number(p.bay || 0),\n  from: String(p.from || \"\"),\n  to: String(p.to || \"\"),\n  user: String(p.user || \"\"),\n  ts: now\n};\nreturn msg;",
    "outputs": 1, "noerr": 0, "x": 460, "y": 140, "wires": [["mqtt-out"]] },

  { "id": "mqtt-out", "type": "mqtt out", "z": "flow-trackbay", "name": "", "topic": "", "qos": "0", "retain": "",
    "broker": "mqtt-local", "x": 720, "y": 140, "wires": [] },

  { "id": "mqtt-in", "type": "mqtt in", "z": "flow-trackbay", "name": "", "topic": "trackbay/reserve", "qos": "0",
    "datatype": "auto", "broker": "mqtt-local", "nl": false, "rap": true, "rh": 0, "x": 200, "y": 240,
    "wires": [["fn-store"]] },

  { "id": "fn-store", "type": "function", "z": "flow-trackbay", "name": "配列に蓄積(flow)",
    "func": "let arr = flow.get('reservations') || [];\narr = arr.concat([msg.payload]);\nflow.set('reservations', arr);\nmsg.payload = arr;\nreturn msg;",
    "outputs": 1, "noerr": 0, "x": 460, "y": 240, "wires": [["fn-view"]] },

  { "id": "fn-view", "type": "function", "z": "flow-trackbay", "name": "表示用に整形",
    "func": "const arr = Array.isArray(msg.payload) ? msg.payload : [];\nmsg.payload = arr.map(r => ({\n  tsText: new Date(r.ts).toLocaleString(),\n  bay: r.bay,\n  from: r.from,\n  to: r.to,\n  user: r.user\n}));\nreturn msg;",
    "outputs": 1, "noerr": 0, "x": 700, "y": 240, "wires": [["ui-tpl"]] },

  { "id": "ui-tpl", "type": "ui_template", "z": "flow-trackbay", "group": "grp-list", "name": "予約一覧(標準)",
    "order": 0, "width": 0, "height": 0,
    "format": "<table style=\"width:100%\">\n  <thead>\n    <tr>\n      <th style=\"text-align:left\">時刻</th>\n      <th style=\"text-align:right\">バース</th>\n      <th style=\"text-align:center\">開始</th>\n      <th style=\"text-align:center\">終了</th>\n      <th style=\"text-align:left\">担当</th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr ng-repeat=\"r in msg.payload track by $index\">\n      <td>{{r.tsText}}</td>\n      <td style=\"text-align:right\">{{r.bay}}</td>\n      <td style=\"text-align:center\">{{r.from}}</td>\n      <td style=\"text-align:center\">{{r.to}}</td>\n      <td>{{r.user}}</td>\n    </tr>\n  </tbody>\n</table>",
    "storeOutMessages": true, "fwdInMessages": true, "resendOnRefresh": true, "templateScope": "local",
    "x": 930, "y": 240, "wires": [[]] },

  { "id": "init", "type": "inject", "z": "flow-trackbay", "name": "起動時に一覧表示", "props": [],
    "repeat": "", "crontab": "", "once": true, "onceDelay": "0.5", "topic": "",
    "x": 190, "y": 300, "wires": [["read-list"]] },

  { "id": "read-list", "type": "function", "z": "flow-trackbay", "name": "flow→msg",
    "func": "msg.payload = flow.get('reservations') || [];\nreturn msg;",
    "outputs": 1, "noerr": 0, "x": 440, "y": 300, "wires": [["fn-view"]] }
]

動作確認

  1. /ui を開き、「予約フォーム」で送信
  2. 右側の一覧に行が追加される
  3. 別ターミナルで MQTT を監視(任意)
mosquitto_sub -t "trackbay/#" -v

トラブルシュート

  • Dashboardが真っ白node-red-dashboard が入っているか/Deployし直し/ブラウザのキャッシュ削除
  • 一覧が出ないui_template のグループ幅(width)が小さすぎないか、fn-store のエラーはないかを debug ノードで確認
  • MQTTが流れない:ブローカのホスト・ポート(127.0.0.1:1883)を再確認。mosquitto_sub で確認

安全性メモ

  • ドメイン/社名/キー/トークン等の固有情報は含まれていません(ローカル接続のみ、例示トピック名)
  • スクショを載せる場合は、IPやURLが写っていないかだけ念のため確認

タグ候補

Node-RED, MQTT, IoT, Dashboard, PoC, Ubuntu, 予約システム

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?