【第1回】センサー&データ収集編(Zabbix + SNMP)
はじめに
オフィスのCO2濃度が1000ppmを超えると意思決定能力が明確に低下するという研究結果があります。コロナ禍以降、換気の重要性が再認識され、オフィスや会議室の空気質をリアルタイムにモニタリングしたいというニーズが急速に高まりました。
しかし、いざ「複数拠点のオフィスをまるごと可視化したい」となると、商用のクラウドサービスは月額が拠点数に比例して跳ね上がり、自前で構築しようとすればVPSとフロント、SSL証明書、WAFと盛りだくさん…と、急に費用も構築負荷も大きくなります。
本記事(第1回)では、
「データ収集サーバーは社内に閉じたまま、可視化フロントだけを月額数百円のさくらレンタルサーバに分離する」 という設計で、この問題を解決した実例を紹介します。
本シリーズは全3回構成で、第1回はセンサー〜データ収集、第2回は可視化フロントエンド(PHP + jQuery)の実装、第3回は同じ基盤を維持したままの React モダン化を扱います。
本記事は、筆者が所属するクイックイタレート株式会社で構築・運用しているオフィス環境モニタリングシステムをベースにした事例紹介です。同種の構成(温湿度CO2の測定と直感的なUI)は弊社の公開事例 温湿度CO2の測定と直感的なUIの開発 でも紹介しています。
システム全体像
まずは全体構成を図示します。
設計の核心:データ収集サーバーをインターネットに出さない
本構成の核心は、「データ収集サーバー(Zabbix)を一切インターネットに公開しない」 という割り切りにあります。
フロントエンドはさくらのレンタルサーバに置き、JSONファイルだけを定期的にアップロードする。これにより、以下の4観点を同時に達成しています。
| 観点 | 効果 |
|---|---|
| 費用 | VPSではなく月数百円のレンタルサーバで済む |
| セキュリティ | フロントの守り(HTTPS終端、WAF、DDoS対策)はさくらにお任せ。Zabbixサーバーはインターネットに露出させない |
| 安定運用 | レンタルサーバは24時間365日の運用実績がある |
| 構築負荷 | SSL証明書の更新、Webサーバーのチューニング、OSパッチ適用などが不要 |
従来構成との比較
「マルチ拠点のオフィス環境を可視化する」という要件に対して、よくある代替案と比較してみます。
| 項目 | 商用クラウドSaaS | VPS自前構築 | 本構成(Zabbix × さくらフロント) |
|---|---|---|---|
| 月額コスト | 拠点数に比例(数千円〜数万円) | 5,000〜10,000円程度 | 数百円〜千円台 |
| 攻撃面 | サービス側が責任 | OS〜アプリまで全層自分で守る | フロントは共用ホスト任せ、データ収集は社内に閉じる |
| データ主権 | クラウド業者依存 | 自分で管理 | 自分で管理(社内Zabbix) |
| 運用負荷 | 低 | 高(パッチ・証明書・ログ監視) | 低(フロント=さくら、収集=Zabbix) |
| アラート機能 | サービス標準 | 自前実装 | Zabbix標準機能 |
| 拠点追加コスト | 課金増 | サーバー設定変更 | 設定配列に1要素追加 |
「フロントエンドが侵害されても、データ収集レイヤーには指一本触れさせない」という構造的な安心感は、運用するうえでの精神衛生上もかなり大きな違いがあります。
これは単なる費用最適化ではなく、責務分離による防御的設計 として捉えるべきものです。
ハードウェア選定:COMET T6540
センサーには、チェコのCOMET社製 WebSensor T6540 を採用しました。
1台で温度・湿度・CO2濃度の3チャンネルを測れる、Ethernet接続のセンサーです。
主要スペックを抜粋します。
| 項目 | 仕様 |
|---|---|
| 温度 | -30〜+80°C / 精度 ±0.6°C |
| 湿度 | 5〜95%RH / 精度 ±2.5%RH |
| CO2 | 0〜5000ppm(オプションで10000ppm)/ 精度 ±(50ppm + 3%) |
| CO2方式 | 二波長NDIR(経年変化を自動補正) |
| 電源 | DC 9〜30V |
| 通信 | Ethernet (10/100BASE-TX) |
| プロトコル | HTTP, SNMPv1, ModbusTCP, SOAP, Syslog, SNTP |
| 推奨校正周期 | 温度2年、湿度1年、CO2 5年 |
選定理由は明確で、
- NDIR方式のCO2センサー:安価な化学式センサーと違い、長期ドリフトが少ない
- SNMP対応:Zabbixと相性が抜群。専用ドライバ不要
- PoE非対応だが安価:今回はPoE不要の現場が多く、コストを優先
- CSV / JSON / XML / Modbus / SOAP まで一通り対応:将来的な連携先を選ばない
の4点。
「とりあえず1拠点に1台置けば全部わかる」という構成にできるのが大きな魅力です。
ネットワーク接続とSNMPによるデータ取得
初期設定
T6540の初期IPアドレスは 192.168.1.213 です。LANに接続し、メーカー提供の TSensor ソフトウェアか、ブラウザで直接 http://192.168.1.213 にアクセスして、運用ネットワークのIPアドレス・サブネット・ゲートウェイ・DNSを設定します。
電源を入れてLCDに値が表示されれば、まずはハードウェア側はOK。
snmpwalk でOIDを確認する
T6540はSNMPv1に対応しており、デフォルトのread communityは public です。
SNMPv1 の public コミュニティは「誰でも値が読める状態」を意味します。
運用前に必ずコミュニティ名を変更し、可能であればセンサーセグメントへのアクセスをZabbixサーバーからのみに絞ってください。
実際にどのOIDから何が取れるかを把握するため、まずは snmpwalk を素朴に叩いてみます。以下、実環境(運用ネットワークに配置した T6540、IPアドレス 192.168.15.90)での実行結果を交えながら解説します。
OIDを指定しないとどうなるか:MIB-2 ツリーのデフォルト走査
snmpwalk は 起点となる OID を省略すると、
自動的に .1.3.6.1.2.1(iso.org.dod.internet.mgmt.mib-2)から走査を開始 します。
これは RFC 1213 で定義された 標準 MIB-2 の起点で、ネットワーク機器ならほぼ例外なく実装している「機器共通の汎用情報」が並ぶツリーです。
コマンド:
# OID を省略 → 自動的に MIB-2 配下をダンプ
$ snmpwalk -v 1 -c public 192.168.15.90
実行結果(実環境での出力):
iso.3.6.1.2.1.1.1.0 = STRING: "Sensor - fw 1-5-7.x "
iso.3.6.1.2.1.1.2.0 = OID: iso.3.6.1.4.1.22626.1.2
iso.3.6.1.2.1.1.3.0 = Timeticks: (473051) 1:18:50.51
iso.3.6.1.2.1.1.4.0 = ""
iso.3.6.1.2.1.1.5.0 = STRING: "Web Sensor"
iso.3.6.1.2.1.1.6.0 = ""
iso.3.6.1.2.1.1.7.0 = INTEGER: 12
iso.3.6.1.2.1.2.1.0 = INTEGER: 1
iso.3.6.1.2.1.2.2.1.1.1 = INTEGER: 1
iso.3.6.1.2.1.2.2.1.2.1 = STRING: "Network"
iso.3.6.1.2.1.2.2.1.3.1 = INTEGER: 6
iso.3.6.1.2.1.2.2.1.4.1 = INTEGER: 1500
iso.3.6.1.2.1.2.2.1.5.1 = Gauge32: 10000000
iso.3.6.1.2.1.2.2.1.6.1 = Hex-STRING: 00 80 A3 E2 70 2E
...(interfaces / ip / icmp / udp のカウンタ類が延々と続く)...
iso.3.6.1.2.1.4.20.1.1.192.168.15.90 = IpAddress: 192.168.15.90
iso.3.6.1.2.1.4.20.1.3.192.168.15.90 = IpAddress: 255.255.255.0
iso.3.6.1.2.1.5.1.0 = Counter32: 4508
iso.3.6.1.2.1.7.1.0 = Counter32: 80
iso.3.6.1.2.1.7.2.0 = Counter32: 645
iso.3.6.1.2.1.7.3.0 = Counter32: 769
iso.3.6.1.2.1.7.4.0 = Counter32: 83
出てきた OID を分類すると、MIB-2 の主要サブツリーが順番に並んでいるのがわかります。
| OIDツリー | グループ名 | 内容 |
|---|---|---|
.1.3.6.1.2.1.1 |
system | デバイス名、説明、sysObjectID、稼働時間(sysUpTime)など |
.1.3.6.1.2.1.2 |
interfaces | NIC情報、MACアドレス、速度、送受信パケット/バイトのカウンタ |
.1.3.6.1.2.1.4 |
ip | IPアドレス、サブネットマスク、ルーティング/IP層の統計 |
.1.3.6.1.2.1.5 |
icmp | ICMP受信/送信の統計 |
.1.3.6.1.2.1.7 |
udp | UDPポート数、受信/送信データグラム数 |
この中で特に目を引くのは以下の3行です。
iso.3.6.1.2.1.1.1.0 = STRING: "Sensor - fw 1-5-7.x " ← sysDescr(機種名・ファームウェア)
iso.3.6.1.2.1.1.2.0 = OID: iso.3.6.1.4.1.22626.1.2 ← sysObjectID(ベンダーMIBのルート)
iso.3.6.1.2.1.1.5.0 = STRING: "Web Sensor" ← sysName
sysObjectID が .1.3.6.1.4.1.22626.1.2 を指しているのが重要なヒントです。
これは「このデバイス固有の情報は IANA enterprise 番号 22626 配下にありますよ」という、機器自身からの道しるべです。
ここで初見だと戸惑うのが、
この出力には温度・湿度・CO2濃度の実測値が一切含まれていない ことです。
system 情報やインターフェース統計はあるのに、肝心のセンサー値が見当たりません。
これは SNMP の設計上ごく自然なことで、
-
MIB-2(
.1.3.6.1.2.1) は RFC で標準化された「機器共通の情報」の置き場所 -
enterprise MIB(
.1.3.6.1.4.1.<ベンダー番号>) はベンダーが独自に定義する「機器固有の情報」の置き場所
という二階建て構造になっており、センサー特有の測定値は後者に配置するのがお作法だからです。COMET社は IANA から enterprise 番号 22626 を割り当てられており、T6540 のセンサー値はすべて .1.3.6.1.4.1.22626 配下に配置されています。
そして snmpwalk にOIDを指定しないと、デフォルト起点の MIB-2 より先には降りてくれない ため、センサー値が欲しければ 明示的に enterprise MIB を指定して再度 walk する 必要があるわけです。
一見するとMIB-2の大量出力は「ノイズ」に見えますが、
sysDescr と sysObjectID の2行は機種同定に直結する重要情報です。
保守現場で「目の前のこの装置、どこのメーカーの何?」と聞かれたら、
まず snmpwalk -v 1 -c public <IP> .1.3.6.1.2.1.1 を叩く、というのは覚えておいて損のない習慣です。
COMETプライベートMIB(enterprise 22626)に絞って再walkする
上で確認した sysObjectID のヒントに従って、今度は起点OIDを指定して walk します。
コマンド:
# COMET enterprise MIB 配下のセンサー値ツリーに絞る
$ snmpwalk -v 1 -c public 192.168.15.90 .1.3.6.1.4.1.22626.1.2.1
実行結果(実環境での出力):
iso.3.6.1.4.1.22626.1.2.1.1.0 = STRING: "24.5"
iso.3.6.1.4.1.22626.1.2.1.2.0 = STRING: "31.7"
iso.3.6.1.4.1.22626.1.2.1.3.0 = STRING: "6.6"
iso.3.6.1.4.1.22626.1.2.1.4.0 = STRING: "592"
iso.3.6.1.4.1.22626.1.2.1.5.0 = STRING: "none"
iso.3.6.1.4.1.22626.1.2.1.6.0 = STRING: "none"
iso.3.6.1.4.1.22626.1.2.1.7.0 = STRING: "none"
iso.3.6.1.4.1.22626.1.2.1.8.0 = STRING: "low"
iso.3.6.1.4.1.22626.1.2.1.9.0 = Hex-STRING: B0 43
iso.3.6.1.4.1.22626.1.2.1.10.0 = STRING: "%RH"
iso.3.6.1.4.1.22626.1.2.1.11.0 = Hex-STRING: B0 43
iso.3.6.1.4.1.22626.1.2.1.12.0 = STRING: "ppm"
iso.3.6.1.4.1.22626.1.2.1.13.0 = STRING: "24.4"
iso.3.6.1.4.1.22626.1.2.1.14.0 = STRING: "31.0"
iso.3.6.1.4.1.22626.1.2.1.15.0 = STRING: "6.3"
iso.3.6.1.4.1.22626.1.2.1.16.0 = STRING: "476"
iso.3.6.1.4.1.22626.1.2.1.17.0 = STRING: "26.4"
iso.3.6.1.4.1.22626.1.2.1.18.0 = STRING: "39.2"
iso.3.6.1.4.1.22626.1.2.1.19.0 = STRING: "11.4"
iso.3.6.1.4.1.22626.1.2.1.20.0 = STRING: "1279"
一気に「センサーらしい値」が出てきました。T6540 は 4チャンネル(温度・湿度・露点・CO2) のマルチセンサーで、サブインデックスが以下のように割り当てられています。
| サブOID | 内容 | 本環境での値 |
|---|---|---|
.1.0 〜 .4.0
|
現在値(ch1:温度 / ch2:湿度 / ch3:露点 / ch4:CO2) | 24.5, 31.7, 6.6, 592 |
.5.0 〜 .8.0
|
各チャンネルのアラーム状態 | none, none, none, low |
.9.0 〜 .12.0
|
各チャンネルの単位表記 | °C, %RH, °C, ppm |
.13.0 〜 .16.0
|
最小値(各チャンネル) | 24.4, 31.0, 6.3, 476 |
.17.0 〜 .20.0
|
最大値(各チャンネル) | 26.4, 39.2, 11.4, 1279 |
つまり上の出力だけで、「現在値・最小値・最大値・アラーム状態・単位」の5系統が一度に読み取れています。運用中に「今朝の最低気温と最大CO2は?」と聞かれても、この .13〜.20 をそのまま見ればOKという設計です。
.9.0 と .11.0 が Hex-STRING: B0 43 となっているのは、°C 記号(0xB0)+ C(0x43) がそのままバイト列で返っているためです。Linux ターミナル(UTF-8)では ° は2バイト文字なので、COMET側が CP-1252/ISO-8859-1 風に1バイトで ° を返してくるとこのように見えます。Zabbix 側では単位は手動で °C を指定するので、この Hex-STRING は 取得しない のが正解です。
数値型(.2.3 配下)との二段構え
T6540 にはもう一つ、数値計算向けの INTEGER 系列 が用意されています。
.1.3.6.1.4.1.22626.1.2.3 配下を walk すると、実値×10 の整数値として取得でき、
STRING をパースするより Zabbix 側で扱いやすくなります。
コマンド:
# 整数エンコード版(各値×10)
$ snmpwalk -v 1 -c public 192.168.15.90 .1.3.6.1.4.1.22626.1.2.3
実行結果(参考値:実値×10 の INTEGER として返る想定):
iso.3.6.1.4.1.22626.1.2.3.1.0 = INTEGER: 245 ← 24.5°C
iso.3.6.1.4.1.22626.1.2.3.2.0 = INTEGER: 317 ← 31.7%RH
iso.3.6.1.4.1.22626.1.2.3.3.0 = INTEGER: 66 ← 6.6°C(露点)
iso.3.6.1.4.1.22626.1.2.3.4.0 = INTEGER: 592 ← 592ppm(CO2は×1)
まとめると、COMET T6540 のセンサー値は以下の 二段構え で公開されています。
| OID系列 | 型 | 人間向け/機械向け |
|---|---|---|
.1.3.6.1.4.1.22626.1.2.1.x.0 |
STRING | 人間がブラウザや snmpget で確認する用("24.5") |
.1.3.6.1.4.1.22626.1.2.3.x.0 |
INTEGER |
Zabbix / 計算処理向け(245 → ×0.1 で実値) |
運用で使うOIDを整理すると以下のようになります。
| OID | 内容 | 型 | Zabbix側の処理 |
|---|---|---|---|
.1.3.6.1.4.1.22626.1.2.3.1.0 |
温度 | INTEGER | ×0.1 でスケーリング |
.1.3.6.1.4.1.22626.1.2.3.2.0 |
湿度 | INTEGER | ×0.1 でスケーリング |
.1.3.6.1.4.1.22626.1.2.3.4.0 |
CO2濃度 | INTEGER | ppm単位そのまま |
数値計算をする以上、INTEGER型を取ってZabbix側で 0.1 倍するほうが、文字列パースより圧倒的にミスが少なく速いです。
Tip : MIBファイルの活用
COMETはMIBファイルを公式サイトで公開しています。
これを/usr/share/snmp/mibs/COMET-MIB.txtなどに配置しsnmp.confでmibs +COMET-MIBを有効にすると、iso.3.6.1.4.1.22626.1.2.3.1.0がCOMET-MIB::tempInt.0のように人間が読める表記になり、ドキュメンテーション性が大幅に向上します。
Zabbixでアイテム登録
ZabbixのWeb UIから、対象のT6540をホストとして登録します。
- Configuration → Hosts → Create host
-
Host name:
comet-tokyo-36F-C1のように、拠点・フロア・エリア・番号を含めた識別性の高い命名にする -
Interfaces に
SNMPを追加し、IPとポート(デフォルト161)を入力 - Templates には自前テンプレート(後述)を割り当てる
アイテム(Item)の設定例(CO2の場合):
| 項目 | 値 |
|---|---|
| Name | CO2 concentration |
| Type | SNMP agent |
| Key | comet.co2[{HOST.HOST}] |
| SNMP OID | .1.3.6.1.4.1.22626.1.2.3.4.0 |
| SNMP version | SNMPv1 |
| SNMP community | {$SNMP_COMMUNITY} |
| Type of information | Numeric (unsigned) |
| Units | ppm |
| Update interval | 5m |
| History | 7d |
| Trends | 365d |
温度・湿度については、
- Type of information: Numeric (float)
- Preprocessing → Custom multiplier:
0.1
を設定して、INTEGER型の生値(234)を実値(23.4)に変換します。
これを テンプレート化(Template: COMET T6540)しておけば、新しい拠点を増やすたびに「ホストを作ってテンプレートを割り当てる」だけで終わります。
Zabbixに溜まったデータを取り出す:history.get API
Zabbixに5分周期でデータが溜まり始めたら、次はそれをフロントエンドに渡すための整形ステップです。
データの流れを時系列で整理するとこうなります:
ZabbixはHTTPベースのJSON-RPC APIを提供しており、
history.get メソッドでアイテムごとの履歴値を取得できます。
Pythonの実装例:
import requests
import time
import json
ZABBIX_URL = "http://zabbix.internal.local/api_jsonrpc.php"
ZABBIX_USER = "api_user"
ZABBIX_PASS = "********"
def login():
payload = {
"jsonrpc": "2.0",
"method": "user.login",
"params": {"user": ZABBIX_USER, "password": ZABBIX_PASS},
"id": 1,
}
r = requests.post(ZABBIX_URL, json=payload)
return r.json()["result"]
def fetch_history(token, itemid, history_type=3, minutes=10):
"""history_type: 0=float, 3=unsigned int (CO2はunsigned、温湿度はfloat)"""
now = int(time.time())
payload = {
"jsonrpc": "2.0",
"method": "history.get",
"params": {
"output": "extend",
"history": history_type,
"itemids": [itemid],
"time_from": now - minutes * 60,
"sortfield": "clock",
"sortorder": "DESC",
"limit": 1, # 最新1件だけ欲しい場合
},
"auth": token,
"id": 2,
}
r = requests.post(ZABBIX_URL, json=payload)
return r.json()["result"]
これを拠点 × フロア × エリア × ポイントの組み合わせ分だけループして取得し、
後述のJSONフォーマットに整形します。
Tip: アイテムIDを毎回問い合わせると遅いので、初回に
item.getで「ホスト名 → CO2/温度/湿度のitemidマップ」を作ってキャッシュしておくと、5分周期のジョブが軽くなります。
JSON整形とアップロード
整形後のJSONはこのような構造にしました(第2回で詳しく扱います):
{
"hokkaido": [],
"tohoku": [],
"tokyo": [
{
"data_area_name": "36F_C1",
"data_date": 1745059500,
"temperature": 23.27,
"humidity": 33.96,
"co2": 482.33
},
{
"data_area_name": "36F_C2",
"data_date": 1745059500,
"temperature": 22.97,
"humidity": 33.60,
"co2": 480.55
}
],
"nagoya": [],
"osaka": [],
"fukuoka": []
}
ファイル名は environmental_data_YYYYMMDD_HHMM00.json という規則。秒は常に 00、分は5の倍数固定です。例:environmental_data_20260419_173500.json。
これを5分ごとに lftp または rsync over SSH でさくらレンタルサーバの environment_data/ ディレクトリへアップロードします。cronで動かすシンプルな例:
# /etc/cron.d/comet_upload
*/5 * * * * comet /opt/comet/bin/collect_and_upload.sh
collect_and_upload.sh は中で以下を行うだけ:
#!/bin/bash
set -e
TS=$(date -u +%Y%m%d_%H%M00)
OUT="/var/spool/comet/environmental_data_${TS}.json"
# 1. Zabbixからデータ取得→JSON生成
python3 /opt/comet/build_json.py "${OUT}"
# 2. さくらへアップロード(鍵認証)
lftp -u "${SAKURA_USER}",, sftp://${SAKURA_HOST} <<EOF
cd www/co2demo/environment_data
put ${OUT}
bye
EOF
# 3. ローカルは7日で削除
find /var/spool/comet/ -name 'environmental_data_*.json' -mtime +7 -delete
これで、Zabbixがデータを集めて、5分ごとにさくらへJSONがアップロードされる という最低限のパイプラインが完成です。

設計上のちょっとした工夫
Zabbix を「現地管理者の計器盤」と「フロントへの供給源」の二役に閉じ込める
このアーキテクチャで重要なのは、Zabbix の役割を 「データ蓄積層 + アラート層 + 現地管理者向けUI」 の3点に閉じ込めて、外部公開のフロントエンドの責務を一切持たせない ことです。役割を整理するとこうなります。
| 役割 | 利用する画面 | 想定ユーザー |
|---|---|---|
| 一般利用者(社員) | さくら上のWebダッシュボード(第2回で解説) | Zabbix を知らなくていい |
| 現地管理者(総務・施設担当) | Zabbix UI を直接使う。閾値調整、アラート確認、生データ確認 | 社内ネットワーク内からアクセス |
| 開発者 | Zabbix API + 整形スクリプト + フロント保守 | 全レイヤーの保守 |
ZabbixのWeb UIは多機能ですが、外部公開には向きません。逆に「内部の人がアラート設定や閾値調整、過去データの確認をする画面」としては非常に優秀で、「ホスト → アイテム → グラフ」というシンプルな階層をクリックしていくだけで、SQLを書かずに値の確認・グラフ表示・閾値設定が完結します。
「攻める画面(外部公開のフロント)」と「守る画面(内部のZabbix)」を完全に分離する──これが本構成の肝です。Zabbix を外部公開してしまうと、結局そこを守るためのリバースプロキシ・WAF・認証強化…と運用負荷が雪だるま式に増えます。Zabbix を内部に閉じ込めることで、フロント側のセキュリティ責務を「JSONを正しく出す」だけに圧縮できる のが、この構成の構造的優位性です。
拠点追加が「ホスト1つ作るだけ」で済む
新しい拠点(例:仙台オフィス)を追加するには、
- T6540を仙台に設置・LANに接続
- ZabbixでホストAdd、テンプレート COMET T6540 を割り当て
- JSON整形スクリプトの拠点コード辞書に
sendaiを1行追加
これだけです。フロントエンド側は後述の通り、設定配列に1要素追加するだけで自動的にUIに表示されます。
第1回まとめ
第1回では、
- COMET T6540 を Ethernet経由で設置
- snmpwalk でOIDを確認し、SNMPv1 で Zabbix から定期ポーリング
- Zabbix の history.get API で値を取り出し、5分刻みのJSONファイル に整形
- rsync/lftp でさくらレンタルサーバにアップロード
という、データ収集パイプラインの設計と実装を見てきました。
ポイントは、「Zabbixを内部に閉じ込めたまま、JSONファイルだけをDMZならぬ"レンタルサーバ"に渡す」 という割り切り。データ収集の堅牢性は Zabbix に任せ、フロント公開のセキュリティはさくら側に任せる──この 責務分離 が、結果的にコスト・セキュリティ・運用負荷のすべてを構造的に軽くしてくれます。
| 担う層 | 何に専念しているか |
|---|---|
| COMET T6540 | センサーとして値を出すことだけ |
| Zabbix | 蓄積・アラート・現地管理者UIの提供だけ |
| 整形スクリプト | JSON生成とアップロードだけ |
| さくらレンタルサーバ | 公開HTTPSとファイル配信だけ |
| フロントPHP/JS(第2回) | JSONを受け取って表示するだけ |
それぞれの層が「自分の専門領域」だけに集中している、疎結合で見通しのいい構造になっています。
第2回では、このJSONを受け取って表示する PHP + Highcharts + Swiper によるフロントエンドの実装、特に4階層ドリルダウンUI、CO2の信号機式可視化、null値の前後平均補間、そして 「DBなし・ビルドなし・依存最小」を支えるPHPコードの工夫 を紹介します。
第3回では、「さくらレンタルサーバで動く」という制約は維持したまま、フロントエンドだけを React に置き換える モダン化の設計判断と実装を扱います。
→次回:【#2】オフィスCO2監視を「内部 Zabbix × 公開フロント」に分離する — COMET T6540 × Zabbix × さくらレンタルサーバの構成
関連記事
関連リンク
本記事は、筆者が所属するクイックイタレート株式会社での開発実績をもとに執筆しています。関連する公開事例は 温湿度CO2の測定と直感的なUIの開発 を、その他の事例は 事例紹介 をご覧ください。



