基于 Modbus 传感器 + Node.js + WebSocket 实现网页端空气质量实时监测系统
·
前言
在物联网项目开发中,将串口传感器数据搬到网页上实时展示是非常常见的需求。本文将从零开始,手把手教大家通过485 转以太网模块 + Node.js 后端 + WebSocket,实现温湿度、PM2.5、TVOC 等空气质量数据的网页端秒级刷新,全程代码开箱即用,新手也能快速复现。
一、硬件准备与接线
1.1 所需硬件
- 空气质量多参数传感器(支持 Modbus RTU 协议,输出温湿度、PM1.0、PM2.5、PM10、TVOC 等数据)
- RS485 转以太网模块(串口服务器,用于将 485 信号转为 TCP 网络数据)
- 12V 直流电源(给传感器和模块供电)
- 网线、485 信号线若干


1.2 接线说明
- 传感器的 A、B 线分别对应连接到 485 转以太网模块的 A、B 端子
- 传感器和模块共地,保证通信稳定
- 模块通过网线连接到电脑所在的局域网
- 给传感器和模块接通 12V 电源
1.3 模块参数配置
串口服务器需要提前配置好参数,确保电脑可以通过 IP 访问:
- 模块 IP:
192.168.1.7(和电脑在同一网段) - 模块端口:
8888 - 串口参数:波特率 9600,数据位 8,停止位 1,无校验

二、软件环境准备2.1 Node.js 安装本项目后端基于 Node.js 开发,需要先安装运行环境: 前往 Node.js 官网下载 LTS 长期支持版 安装时全程默认下一步即可 验证安装:按下 Win+R 输入 cmd 打开命令提示符,输入以下命令,输出版本号即安装成功
node -v
npm -v
2.2 VS Code 安装(可选)
推荐使用 VS Code 编写和运行代码,也可以根据自己习惯使用其他编辑器。
三、项目搭建与代码实现
3.1 创建项目文件夹
在电脑任意位置新建一个文件夹,命名为 sensor-demo,作为项目根目录。
3.2 安装 WebSocket 依赖
- 在项目文件夹内右键,选择「在终端中打开」
- 输入以下命令,安装
ws依赖包
npm install ws
安装完成后,文件夹内会自动生成 node_modules 目录和 package.json 文件。
3.3 后端服务代码(server.js)
在项目根目录新建 server.js 文件,作用是:
- 通过 TCP 连接 485 模块,定时发送 Modbus 读取指令
- 解析传感器返回的十六进制数据,换算成实际物理值
- 启动 WebSocket 服务,将数据实时推送到网页端
完整代码如下:
const net = require('net');
const WebSocket = require('ws');
// ========== 配置参数(根据自己的硬件修改)==========
const MODULE_IP = '192.168.1.7'; // 485模块IP地址
const MODULE_PORT = 8888; // 模块TCP端口
const WS_PORT = 8080; // WebSocket服务端口
const READ_INTERVAL = 1000; // 数据读取间隔(毫秒),1000=1秒
// Modbus RTU 读取全部寄存器的指令(设备地址1,读取14个寄存器)
const READ_CMD = Buffer.from('01030001000E95CE', 'hex');
// ========== 启动 WebSocket 服务 ==========
const wss = new WebSocket.Server({ port: WS_PORT });
console.log(`✅ WebSocket服务已启动,端口${WS_PORT}`);
console.log(`📡 正在连接传感器模块 ${MODULE_IP}:${MODULE_PORT}...`);
// ========== 建立TCP连接,读取传感器数据 ==========
const client = new net.Socket();
client.connect(MODULE_PORT, MODULE_IP, () => {
console.log('✅ 模块连接成功,开始定时读取数据');
// 定时循环:每隔指定时间发送一次读取指令
setInterval(() => {
if (client.writable) client.write(READ_CMD);
}, READ_INTERVAL);
});
// 接收并解析传感器返回的数据
client.on('data', (buf) => {
// 数据长度不足则跳过,防止解析错误
if (buf.length < 30) return;
// 按寄存器位置解析并换算成实际数值
const sensorData = {
temp: (buf.readUInt16BE(3) - 2000) / 100, // 温度,单位℃
humi: buf.readUInt16BE(5) / 100, // 湿度,单位%RH
pm1_0: buf.readUInt16BE(15), // PM1.0,单位μg/m³
pm2_5: buf.readUInt16BE(17), // PM2.5,单位μg/m³
pm10: buf.readUInt16BE(19), // PM10,单位μg/m³
tvoc: buf.readUInt16BE(29) / 100, // TVOC,单位mg/m³
time: new Date().toLocaleTimeString() // 数据更新时间
};
// 将数据广播给所有已连接的网页客户端
wss.clients.forEach(ws => {
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify(sensorData));
}
});
// 终端控制台打印数据,方便调试
console.log(`🌡 ${sensorData.temp.toFixed(1)}℃ 💧 ${sensorData.humi.toFixed(1)}% 🌫 PM2.5: ${sensorData.pm2_5} μg/m³`);
});
// 连接异常处理
client.on('error', (err) => {
console.error('❌ 模块连接失败:', err.message);
});
client.on('close', () => {
console.log('🔌 与模块的连接已断开');
});
3.4 前端页面代码(index.html)
在项目根目录新建 index.html 文件,作用是:
- 连接后端 WebSocket 服务
- 接收实时数据并渲染到页面
- 展示美观的卡片式监测界面
完整代码如下:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>空气质量实时监测系统</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: "Microsoft Yahei", sans-serif;
}
body {
background-color: #f0f2f5;
padding: 40px 20px;
}
.container {
max-width: 600px;
margin: 0 auto;
background: #fff;
border-radius: 12px;
padding: 30px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
}
h2 {
text-align: center;
color: #333;
margin-bottom: 20px;
}
.status {
text-align: center;
padding: 10px;
border-radius: 6px;
margin-bottom: 25px;
background-color: #e3f2fd;
color: #1565c0;
font-size: 16px;
}
.status.error {
background-color: #ffebee;
color: #c62828;
}
.status.success {
background-color: #e8f5e9;
color: #2e7d32;
}
.card {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px 20px;
margin: 12px 0;
background-color: #f8f9fa;
border-radius: 8px;
border-left: 4px solid #1976d2;
}
.label {
font-size: 18px;
color: #555;
}
.value {
font-size: 26px;
font-weight: bold;
color: #1976d2;
}
.unit {
font-size: 14px;
color: #999;
margin-left: 4px;
}
.time {
text-align: center;
color: #999;
margin-top: 20px;
font-size: 14px;
}
</style>
</head>
<body>
<div class="container">
<h2>空气质量实时监测</h2>
<div class="status" id="status">正在连接服务...</div>
<div class="card">
<span class="label">环境温度</span>
<span><span class="value" id="temp">--</span><span class="unit">℃</span></span>
</div>
<div class="card">
<span class="label">相对湿度</span>
<span><span class="value" id="humi">--</span><span class="unit">%RH</span></span>
</div>
<div class="card">
<span class="label">PM1.0</span>
<span><span class="value" id="pm1">--</span><span class="unit">μg/m³</span></span>
</div>
<div class="card">
<span class="label">PM2.5</span>
<span><span class="value" id="pm25">--</span><span class="unit">μg/m³</span></span>
</div>
<div class="card">
<span class="label">PM10</span>
<span><span class="value" id="pm10">--</span><span class="unit">μg/m³</span></span>
</div>
<div class="card">
<span class="label">TVOC</span>
<span><span class="value" id="tvoc">--</span><span class="unit">mg/m³</span></span>
</div>
<div class="time">更新时间:<span id="time">--</span></div>
</div>
<script>
// 连接本地WebSocket服务
const ws = new WebSocket('ws://localhost:8080');
const statusEl = document.getElementById('status');
// 连接成功回调
ws.onopen = function() {
statusEl.textContent = '✅ 已连接,数据实时更新中';
statusEl.className = 'status success';
};
// 收到数据回调,更新页面
ws.onmessage = function(e) {
const data = JSON.parse(e.data);
document.getElementById('temp').textContent = data.temp.toFixed(2);
document.getElementById('humi').textContent = data.humi.toFixed(2);
document.getElementById('pm1').textContent = data.pm1_0;
document.getElementById('pm25').textContent = data.pm2_5;
document.getElementById('pm10').textContent = data.pm10;
document.getElementById('tvoc').textContent = data.tvoc.toFixed(2);
document.getElementById('time').textContent = data.time;
};
// 连接断开回调
ws.onclose = function() {
statusEl.textContent = '❌ 连接断开,请检查后端服务';
statusEl.className = 'status error';
};
// 连接错误回调
ws.onerror = function() {
statusEl.textContent = '❌ 服务连接失败';
statusEl.className = 'status error';
};
</script>
</body>
</html>
四、运行与效果展示
4.1 启动后端服务
- 在 VS Code 中打开项目文件夹,按下
Ctrl+~打开终端 - 在终端中输入以下命令,启动后端服务
运行
node server.js
- 终端出现以下提示,且每秒刷新一次数据,说明运行成功

4.2 打开网页查看实时数据
- 直接双击项目中的
index.html文件,用浏览器打开 - 页面顶部显示「已连接,数据实时更新中」,所有参数每秒自动刷新

五、常用参数修改说明
5.1 修改数据刷新频率
打开 server.js 文件,修改第 7 行的数值即可,单位为毫秒:
// 1000 = 1秒刷新一次
// 2000 = 2秒刷新一次
// 500 = 0.5秒刷新一次
const READ_INTERVAL = 1000;
修改后保存文件,重启服务生效。
5.2 修改模块 IP 和端口
如果你的串口服务器 IP 或端口不是默认值,修改 server.js 中对应配置即可:
const MODULE_IP = '192.168.1.7'; // 改成你的模块IP
const MODULE_PORT = 8888; // 改成你的模块端口
六、常见问题排查
-
终端提示连接失败
- 检查模块是否上电,网线是否插好
- 确认电脑和模块在同一局域网,能 ping 通模块 IP
- 检查 IP 和端口是否填写正确
-
网页显示连接失败
- 确认后端服务已启动,没有关闭终端窗口
- 刷新浏览器页面重试
- 检查防火墙是否拦截了 8080 端口
-
数据全为 0 或数值不对
- 检查传感器接线是否正确,A/B 线是否接反
- 确认传感器 Modbus 寄存器地址和代码中解析位置对应
- 用网络调试助手发指令测试,确认传感器本身返回正常
总结与扩展
本项目通过极简的代码实现了串口传感器数据的网页端实时展示,核心思路是「TCP 采集数据 → 后端解析换算 → WebSocket 实时推送」,架构轻量且易于扩展。
后续可以在此基础上扩展更多功能:
- 接入 ECharts 绘制历史数据曲线图
- 添加数值超标报警功能(弹窗提示、播放提示音)
- 将数据存入 SQLite 或 MySQL 数据库,支持历史查询
- 部署到云服务器,实现外网随时随地访问
更多推荐


所有评论(0)