DeHydroponics 并不是一个需要昂贵商业控制器的项目,它的核心思路是把水培装置拆成传感器层、传输层、服务层和展示层。Node.js 作为服务层可以同时处理设备消息、业务逻辑和 Web 接口,省去了多语言切换的麻烦。整个系统围绕 MQTT 消息驱动,传感器数据从 ESP32 端发出,经过 Broker 到达 Node.js 服务端,服务端负责解析、存储、判断阈值,并把控制指令反向发布给执行设备。

一、硬件选型与系统分层
水培装置需要持续监测的营养液参数通常包括温度、pH 值和 EC 值(电导率),有时还要加上环境光照和空气温湿度。执行机构则包含水泵、气泵、补光灯和电磁阀。在硬件端,使用 ESP32 作为主控,通过模拟输入或 I2C 接口读取各类传感器,再通过 Wi-Fi 把数据发布到 MQTT Broker。ESP32 的模拟输入精度有限,pH 和 EC 传感器需要经过校准并配合温度补偿,这部分通常需要在固件层完成。
系统分层上,硬件层负责传感器采样和继电器开关,可以用 Arduino IDE 或 PlatformIO 编写 C++ 固件。传输层选用 MQTT 协议,主题设计遵循统一前缀,例如 dehydro/sensor/ph、dehydro/sensor/ec、dehydro/actuator/pump。服务层就是本文的主角 Node.js,展示层则是浏览器页面。MQTT 相比 HTTP 更适合物联网场景,因为它是轻量级的发布订阅协议,支持断线重连和低带宽通信,而且 Broker 可以使用开源的 Mosquitto,部署成本很低。
为什么不让 Node.js 直接操作 GPIO?因为服务端通常运行在树莓派、NAS 或云服务器上,而传感器和执行机构分布在种植架附近。两者物理分离后,采用 MQTT 指令下发是最自然的方式。设备端订阅自己的执行主题,收到 JSON 指令后驱动继电器,并在完成后回传状态。这样即使服务端重启,设备端依然可以按照最后一次指令维持运行。
二、Node.js 服务端核心:MQTT 订阅与数据存储
服务端首先需要安装依赖,包括 mqtt、express、sqlite3、node-cron 和 socket.io。入口文件中连接 MQTT Broker,订阅传感器主题。消息回调里需要做 JSON 解析和异常捕获,因为设备端可能发送不完整的数据包。
const mqtt = require('mqtt');
const client = mqtt.connect('mqtt://127.0.0.1:1883');
client.on('connect', () => {
console.log('MQTT connected');
client.subscribe('dehydro/sensor/#', { qos: 1 });
});
client.on('message', (topic, message) => {
try {
const payload = JSON.parse(message.toString());
console.log(topic, payload);
saveSensorData(topic, payload);
} catch (err) {
console.error('invalid message', err.message);
}
});
数据存储选用 SQLite 的原因在于它轻量、无需独立数据库进程,单文件即可保存,适合单机部署和原型验证。建表时按传感器类型和数值分开存储,同时保留时间戳和单位。后续如果数据量增大,可以平滑迁移到 PostgreSQL 或时序数据库 InfluxDB。
const sqlite3 = require('sqlite3').verbose();
const db = new sqlite3.Database('./dehydro.db');
db.serialize(() => {
db.run(`CREATE TABLE IF NOT EXISTS sensor_readings (
id INTEGER PRIMARY KEY AUTOINCREMENT,
sensor_type TEXT NOT NULL,
value REAL NOT NULL,
unit TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)`);
});
function saveSensorData(topic, payload) {
const type = topic.split('/').pop();
const stmt = db.prepare('INSERT INTO sensor_readings (sensor_type, value, unit) VALUES (?, ?, ?)');
stmt.run(type, payload.value, payload.unit || '');
stmt.finalize();
}
MQTT 消息可能因为网络波动出现乱序或重复,服务端不能盲目信任每一条数据。可以在保存前做数值范围过滤,例如 pH 值合理范围是 0 到 14,EC 值通常不会超过 5000。超出范围的数据直接丢弃,并记录一条警告日志。对于时间戳,最好以服务端接收时间为准,避免设备端时钟不准带来的混乱。
三、自动控制:定时任务与反向指令
水培系统的日常维护离不开定时任务,例如补光灯每天固定开关、营养液循环泵每隔几小时运行一段时间。Node.js 的 node-cron 模块提供了类似 crontab 的表达式能力,可以方便地定义这些周期任务。下面代码展示了光照控制和水泵循环的典型配置。
const cron = require('node-cron');
cron.schedule('0 6 * * *', () => {
client.publish('dehydro/actuator/light', JSON.stringify({ state: 'on' }));
});
cron.schedule('0 22 * * *', () => {
client.publish('dehydro/actuator/light', JSON.stringify({ state: 'off' }));
});
cron.schedule('0 */2 * * *', () => {
client.publish('dehydro/actuator/pump', JSON.stringify({ state: 'on', duration: 900 }));
});
这里的关键设计是把设备端做成指令执行器,而不是由服务端直接控制 GPIO。设备端收到 dehydro/actuator/pump 的消息后,根据 duration 字段打开继电器,并在倒计时结束后自动关闭。这样即使 MQTT 连接暂时中断,水泵也不会一直运行,安全性更高。设备端还可以在关闭时回传一条状态消息,服务端据此更新界面中的设备状态。
除了定时控制,异常告警也是自动控制的重要部分。例如当 pH 值低于 5.5 或高于 7.5 时,服务端可以触发告警逻辑。最简单的做法是发送邮件或调用 Webhook,同时发布一条紧急指令关闭营养液循环泵。阈值判断可以写在 MQTT 消息回调中,也可以定期从数据库查询最新值。前者及时性更好,后者可以避免瞬时波动造成的误报。
四、Web 控制台与实时监控
Express 负责提供 RESTful API,前端页面通过接口获取历史数据和设备状态。下面是一个返回最新传感器记录的接口示例,它从 SQLite 中查询最近 10 条数据并返回 JSON。生产环境中建议增加分页参数和身份验证。
const express = require('express');
const app = express();
app.use(express.json());
app.get('/api/sensors/latest', (req, res) => {
db.all('SELECT sensor_type, value, unit, created_at FROM sensor_readings ORDER BY id DESC LIMIT 10', (err, rows) => {
if (err) return res.status(500).json({ error: err.message });
res.json(rows);
});
});
app.listen(3000, () => console.log('API on 3000'));
实时监控则需要 Socket.IO,它能把 MQTT 收到的新数据立即推送到浏览器。在 MQTT 消息回调中调用 io.emit('sensor-data', payload),前端监听同名事件并更新页面。这样可以避免前端频繁轮询接口,减少服务端压力。前端代码可以非常简洁,下面是一个基本页面。
<!DOCTYPE html>
<html>
<body>
<h3>实时环境数据</h3>
<div id="data"></div>
<script src="/socket.io/socket.io.js"></script>
<script>
const socket = io();
socket.on('sensor-data', (data) => {
document.getElementById('data').innerText = JSON.stringify(data);
});
</script>
</body>
</html>
部署时可以使用 PM2 守护 Node.js 进程,配合 Nginx 做反向代理和 HTTPS 加密。传感器数据属于隐私信息,不应该在公网上明文传输。如果希望进一步可视化历史趋势,可以把数据同时写入 InfluxDB,再用 Grafana 展示,但这样会增加运维复杂度,对于个人项目而言 SQLite 配合简单的折线图已经足够。
整个 DeHydroponics 项目把水培控制的核心逻辑集中在 Node.js 服务端,设备端保持简单,前端保持轻量。这种分层方式不仅让系统容易调试,也为后续扩展更多传感器或执行器留出了空间。只要理解了 MQTT 消息流和定时任务的配合,就能基于这套骨架快速搭建自己的智能种植环境。
Node.js水培系统DeHydroponics修改时间:2026-09-19 18:09:48