|
物联网数据可视化:Grafana + InfluxDB 搭建实时监控大屏

物联网数据可视化:Grafana + InfluxDB 搭建实时监控大屏

为什么 IoT 项目需要数据可视化大屏?

做物联网项目的同学一定遇到过这个问题:传感器数据采了一大堆,存在数据库里落灰,老板或客户问”现在温度多少?湿度趋势怎样?“你只能翻日志查半天。

数据不可视化,就等于没有数据。

今天这篇,我就带你从零搭建一套完整的 IoT 数据可视化系统。我们使用 Grafana + InfluxDB 这对黄金组合,ESP32 采集传感器数据后通过 HTTP 协议写入 InfluxDB,再用 Grafana 创建炫酷的实时监控大屏。整个过程不需要写一行前端代码,纯配置就能搞定。

整体架构

ESP32 + BME280 传感器

        │  HTTP POST /api/v2/write

   InfluxDB 2.x
   (时序数据库)

        │  查询

    Grafana 10.x
   (可视化大屏)
  • ESP32:硬件端,负责采集温湿度、气压数据,通过 WiFi 发送 HTTP 请求写入 InfluxDB
  • InfluxDB 2.x:专门存储时序数据的数据库,写入速度快、压缩率高,天然适合传感器场景
  • Grafana:开源可视化工具,内置 InfluxDB 插件,拖拽即可生成图表

硬件清单

硬件数量说明
ESP32 开发板(ESP32-WROOM-32)1主控,自带 WiFi
BME280 传感器模块1温湿度 + 气压三合一,I2C 接口
杜邦线(母对母)4连接传感器
面包板1原型搭建

第一步:安装 InfluxDB 2.x

推荐用 Docker 部署,一条命令搞定:

docker run -d \
  --name influxdb2 \
  -p 8086:8086 \
  -v influxdb-storage:/var/lib/influxdb2 \
  -v influxdb-config:/etc/influxdb2 \
  -e DOCKER_INFLUXDB_INIT_MODE=setup \
  -e DOCKER_INFLUXDB_INIT_USERNAME=admin \
  -e DOCKER_INFLUXDB_INIT_PASSWORD=MakerOnsite2026 \
  -e DOCKER_INFLUXDB_INIT_ORG=makeronsite \
  -e DOCKER_INFLUXDB_INIT_BUCKET=iot-sensors \
  influxdb:2.7

启动成功后,浏览器访问 http://localhost:8086,用 admin / MakerOnsite2026 登录,就能看到 InfluxDB 的管理界面。

创建 API Token

登录后进入 Load Data → API Tokens,点击 Generate API Token → Read/Write Token,创建一个新的读写权限 Token。这个 Token 后续 ESP32 写入数据和 Grafana 读取数据都要用到。

⚠️ 注意:Token 创建后只显示一次,务必复制保存!

第二步:ESP32 采集数据并写入 InfluxDB

我们用 Arduino 框架开发 ESP32,通过 I2C 读取 BME280 传感器数据,然后用 HTTP POST 将数据写入 InfluxDB 的 API。

接线方式

BME280 引脚ESP32 引脚
VCC3.3V
GNDGND
SCLGPIO 22
SDAGPIO 21

Arduino 代码

#include <WiFi.h>
#include <HTTPClient.h>
#include <Wire.h>
#include <Adafruit_BME280.h>

// WiFi 配置
const char* ssid = "YourWiFiSSID";
const char* password = "YourWiFiPassword";

// InfluxDB 配置
const char* influxdb_url = "http://192.168.1.100:8086";
const char* org = "makeronsite";
const char* bucket = "iot-sensors";
const char* token = "your-api-token-here";

// BME280 传感器
#define SEALEVELPRESSURE_HPA 1013.25
Adafruit_BME280 bme;

// 数据发送间隔(毫秒)
const int SEND_INTERVAL = 10000;
unsigned long lastSend = 0;

void setup() {
  Serial.begin(115200);

  // 连接 WiFi
  WiFi.begin(ssid, password);
  Serial.print("Connecting to WiFi");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("\nWiFi connected, IP: " + WiFi.localIP().toString());

  // 初始化 BME280
  if (!bme.begin(0x76)) {
    Serial.println("BME280 sensor not found!");
    while (1) delay(100);
  }
  Serial.println("BME280 sensor initialized.");
}

void loop() {
  unsigned long now = millis();

  if (now - lastSend >= SEND_INTERVAL) {
    lastSend = now;
    sendToInfluxDB();
  }

  delay(100);
}

void sendToInfluxDB() {
  // 读取传感器数据
  float temperature = bme.readTemperature();
  float humidity = bme.readHumidity();
  float pressure = bme.readPressure() / 100.0F;

  if (isnan(temperature) || isnan(humidity)) {
    Serial.println("Failed to read sensor data!");
    return;
  }

  Serial.printf("Temp: %.2f°C | Humidity: %.2f%% | Pressure: %.2fhPa\n",
                temperature, humidity, pressure);

  // 构建 InfluxDB Line Protocol
  String lineProtocol = "bme280,location=living_room ";
  lineProtocol += "temperature=" + String(temperature) + ",";
  lineProtocol += "humidity=" + String(humidity) + ",";
  lineProtocol += "pressure=" + String(pressure);

  // 构建 InfluxDB v2 API URL
  String writeUrl = String(influxdb_url) + "/api/v2/write";
  writeUrl += "?org=" + String(org);
  writeUrl += "&bucket=" + String(bucket);
  writeUrl += "&precision=ms";

  HTTPClient http;
  http.begin(writeUrl);
  http.addHeader("Authorization", "Token " + String(token));
  http.addHeader("Content-Type", "text/plain; charset=utf-8");

  int httpResponseCode = http.POST(lineProtocol);

  if (httpResponseCode > 0) {
    Serial.printf("InfluxDB write success, HTTP code: %d\n", httpResponseCode);
  } else {
    Serial.printf("InfluxDB write failed, HTTP code: %d\n", httpResponseCode);
  }

  http.end();
}

代码要点解析

  1. Line Protocol:这是 InfluxDB 的数据格式,格式为 measurement,tag_key=tag_value field_key=field_value timestamp
    • measurement 类似 SQL 中的表名,这里是 bme280
    • tag 是索引字段(如 location),用于快速筛选
    • field 是实际数值(如 temperature、humidity)
  2. 精度参数precision=ms 表示时间戳精确到毫秒
  3. Token 认证:InfluxDB 2.x 要求每次请求都带上 Authorization Header

💡 小贴士:如果你的 ESP32 内存紧张,可以把 SEND_INTERVAL 调整为 30000(30 秒)或 60000(1 分钟),降低写入频率。

第三步:安装 Grafana 并连接 InfluxDB

Docker 安装 Grafana

docker run -d \
  --name grafana \
  -p 3000:3000 \
  -v grafana-storage:/var/lib/grafana \
  grafana/grafana:10.4.0

访问 http://localhost:3000,默认用户名和密码都是 admin,首次登录会要求修改密码。

添加 InfluxDB 数据源

  1. 左侧菜单点击 Connections → Data Sources
  2. 点击 Add data source,搜索并选择 InfluxDB
  3. 关键配置:
配置项
Query LanguageFlux
URLhttp://192.168.1.100:8086
Organizationmakeronsite
Token你的 API Token
Default Bucketiot-sensors
  1. 点击 Save & Test,看到 “Data source is working” 就说明连接成功!

第四步:创建监控 Dashboard

创建第一个面板:实时温度

  1. 点击左侧菜单 Dashboards → Create Dashboard → Add visualization
  2. 选择刚才创建的 InfluxDB 数据源
  3. 在 Query Editor 中使用 Flux 查询语言:
from(bucket: "iot-sensors")
  |> range(start: -1h)
  |> filter(fn: (r) => r["_measurement"] == "bme280")
  |> filter(fn: (r) => r["_field"] == "temperature")
  |> aggregateWindow(every: 1m, fn: mean, createEmpty: false)
  |> yield(name: "mean")
  1. 右侧 Panel 类型选择 Time series(时序折线图)
  2. 设置 Panel title 为 “Living Room Temperature”
  3. 调整颜色、单位(°C)、坐标轴

添加更多面板

点击 Add → Visualization,用同样的方法添加:

湿度面板:

from(bucket: "iot-sensors")
  |> range(start: -1h)
  |> filter(fn: (r) => r["_measurement"] == "bme280")
  |> filter(fn: (r) => r["_field"] == "humidity")
  |> aggregateWindow(every: 1m, fn: mean, createEmpty: false)
  |> yield(name: "mean")

Panel 类型选 Gauge(仪表盘),单位设置为 Percent (0-100),颜色阈值设为 30%-70% 绿色、其他红色。

气压面板:

from(bucket: "iot-sensors")
  |> range(start: -1h)
  |> filter(fn: (r) => r["_measurement"] == "bme280")
  |> filter(fn: (r) => r["_field"] == "pressure")
  |> aggregateWindow(every: 1m, fn: mean, createEmpty: false)
  |> yield(name: "mean")

Panel 类型选 Stat(单值显示),单位 hPa

组合布局

Grafana 支持拖拽调整面板大小和位置。建议布局:

┌──────────────────────┬──────────────────────┐
│   温度折线图 (宽)    │   湿度仪表盘 (小)    │
├──────────────────────┴──────────────────────┤
│              气压单值显示                    │
└──────────────────────────────────────────────┘

保存 Dashboard 并命名为 IoT Sensors Dashboard

第五步:进阶玩法

添加告警规则

Grafana 支持内置告警。点击面板标题 → Alert → Create Alert Rule

  • 规则:当温度 > 35°C 持续 5 分钟时触发
  • 通知渠道:可配置 Email、Webhook、钉钉、企业微信等

多传感器对比

如果你有多个 ESP32 节点,在 Line Protocol 中使用不同的 location tag:

// 节点 2(室外)
String lineProtocol = "bme280,location=outdoor ";
lineProtocol += "temperature=" + String(temperature) + ",";

然后在 Grafana 中用 $location 变量做筛选,或用以下查询同时展示多个位置:

from(bucket: "iot-sensors")
  |> range(start: -1h)
  |> filter(fn: (r) => r["_measurement"] == "bme280")
  |> filter(fn: (r) => r["_field"] == "temperature")
  |> group(columns: ["location"])
  |> aggregateWindow(every: 5m, fn: mean, createEmpty: false)

历史数据保留策略

InfluxDB 默认数据永久保存,对于高频采集场景会占用大量磁盘。可以创建保留策略(Retention Policy):

# 通过 InfluxDB CLI 创建 90 天自动清理策略
influx v1 dbrp create \
  --bucket-id your-bucket-id \
  --db iot-sensors \
  --rp 90d \
  --default

常见问题排查

问题 1:ESP32 写入返回 HTTP 401

原因:Token 不正确或过期。

排查

# 测试 Token 是否有效
curl -s http://192.168.1.100:8086/api/v2/buckets \
  -H "Authorization: Token YOUR_TOKEN"

如果返回 401,重新生成 Token 并更新 ESP32 代码。

问题 2:Grafana 查不到数据

原因:Bucket 名称不匹配或查询语句有误。

排查

  1. 在 InfluxDB Web UI 的 Data Explorer 中手动运行查询,确认数据已写入
  2. 检查 Grafana 数据源的 Bucket 名称是否与 InfluxDB 中一致
  3. 查询中的 _measurement 名称要与 Line Protocol 中一致

问题 3:数据时间戳不对

原因:ESP32 没有 NTP 时间同步,写入的时间戳不准确。

解决:不传时间戳,让 InfluxDB 使用服务器时间:

// 不追加时间戳,InfluxDB 自动使用写入时的服务器时间
String lineProtocol = "bme280,location=living_room ";
lineProtocol += "temperature=" + String(temperature) + ",";
// URL 中去掉 precision=ms 参数

问题 4:图表数据断断续续

原因:ESP32 WiFi 连接不稳定或发送间隔过长。

排查

  1. 串口监视器中查看 InfluxDB write success 的日志频率
  2. 检查 ESP32 与 InfluxDB 服务器的网络延迟
  3. 考虑在 ESP32 端添加数据缓存,网络恢复后补发

总结

今天我们完成了一套完整的 IoT 数据可视化方案:

  • InfluxDB 负责高效存储传感器时序数据,写入速度轻松达到每秒数万条
  • Grafana 零代码拖拽生成漂亮的监控面板
  • ESP32 + BME280 提供实时环境数据采集

这套方案的优势在于:

  1. 低成本:全部开源软件,硬件成本不到 50 元
  2. 可扩展:轻松接入几十甚至上百个传感器节点
  3. 实时性:数据从采集到展示延迟不到 10 秒
  4. 零前端:不需要写 HTML/CSS/JS,配置即可

下一步你可以尝试接入 MQTT Broker(如 EMQX),让数据通过 MQTT → Telegraf → InfluxDB 的管道流入,实现更标准化的 IoT 数据流水线。

Happy Making!🔧