跳转到内容

Node-RED 调度逻辑

Node-RED 调度逻辑

本节介绍如何通过 Node-RED 实现广播系统的调度逻辑。学习完成后,您将能够:

  • 在 Node-RED 中创建定时触发流程
  • 配置工作日/节假日不同调度策略
  • 实现广播系统状态的可视化仪表板
  • 通过仪表板控制广播定时计划

在开始本节之前,请确保:

  • 已完成 Node-RED 基础学习
  • 了解 Node-RED 的 inject 节点和 function 节点
  • Mosquitto 或 EMQX Broker 已运行
  • 已完成 ESP32 端的定时调度配置

使用 Node-RED 的 inject 节点实现定时触发:

{
"id": "inject_timer",
"type": "inject",
"repeat": "custom", // 自定义间隔
"crontab": "0 6 * * 1-5", // cron 表达式:工作日 6:00
"payload": "play",
"payload_type": "str",
"topic": "factory/broadcast/schedule"
}

cron 表达式示例

cron 表达式含义
0 6 * * 1-5工作日 6:00
30 7 * * *每天 7:30
0 8 * * 6,0周末 8:00
0 12 * * 1-5工作日 12:00
0 17 * * 1-5工作日 17:00
0 21 * * *每天 21:00
┌─────────────────────┐
Inject ──────→│ Schedule Check ├──────→ MQTT Out
(Timer) │ (Function Node) │
└─────────────────────┘
┌─────────────────────┐
│ Log to File/Debug │
└─────────────────────┘

Schedule Check Function 节点

// 检查当前时间并执行调度
const now = new Date();
const hour = now.getHours();
const minute = now.getMinutes();
const day = now.getDay(); // 0=周日, 1=周一, ...
// 工作日检查(周一至周五)
const isWeekday = day >= 1 && day <= 5;
// 解析 cron 触发时间
const triggerHour = parseInt(msg.topic.split(' ')[0]);
const triggerMinute = parseInt(msg.topic.split(' ')[1]);
if (hour === triggerHour && minute === triggerMinute) {
msg.payload = {
hour: hour,
minute: minute,
weekday: isWeekday,
action: "schedule_trigger"
};
return msg;
}
return null;

创建完整的调度管理流程:

{
"flows": [
{
"id": "schedule_manager",
"nodes": [
// 1. 定时触发节点
{
"id": "timer_0600_weekdays",
"type": "inject",
"crontab": "0 6 * * 1-5",
"payload": "{\"action\":\"play\",\"station\":0,\"volume\":40}",
"topic": "factory/broadcast/control"
},
{
"id": "timer_0730_weekdays",
"type": "inject",
"crontab": "30 7 * * 1-5",
"payload": "{\"action\":\"play\",\"station\":1,\"volume\":60}",
"topic": "factory/broadcast/control"
},
{
"id": "timer_1200_weekdays",
"type": "inject",
"crontab": "0 12 * * 1-5",
"payload": "{\"action\":\"play\",\"station\":2,\"volume\":50}",
"topic": "factory/broadcast/control"
},
{
"id": "timer_1700_weekdays",
"type": "inject",
"crontab": "0 17 * * 1-5",
"payload": "{\"action\":\"stop\"}",
"topic": "factory/broadcast/control"
},
// 周末配置
{
"id": "timer_0900_weekends",
"type": "inject",
"crontab": "0 9 * * 6,0",
"payload": "{\"action\":\"play\",\"station\":1,\"volume\":30}",
"topic": "factory/broadcast/control"
},
{
"id": "timer_2100_all",
"type": "inject",
"crontab": "0 21 * * *",
"payload": "{\"action\":\"stop\"}",
"topic": "factory/broadcast/control"
},
// MQTT 输出
{
"id": "mqtt_out",
"type": "mqtt out",
"server": "localhost",
"topic": "factory/broadcast/control",
"qos": 1
},
// 调试输出
{
"id": "debug_out",
"type": "debug"
}
],
"connections": [
{"from": "timer_0600_weekdays", "to": "mqtt_out"},
{"from": "timer_0730_weekdays", "to": "mqtt_out"},
{"from": "timer_1200_weekdays", "to": "mqtt_out"},
{"from": "timer_1700_weekdays", "to": "mqtt_out"},
{"from": "timer_0900_weekends", "to": "mqtt_out"},
{"from": "timer_2100_all", "to": "mqtt_out"}
]
}
]
}

创建 Node-RED 仪表板来管理广播调度:

// ui_group: "广播系统"
// ui_tab: "工业广播"
// 1. 手动控制区域
{
"id": "btn_play",
"type": "ui_button",
"group": "broadcast_control",
"label": "播放",
"payload": "play",
"topic": "factory/broadcast/play"
}
// 2. 电台选择
{
"id": "dropdown_station",
"type": "ui_dropdown",
"group": "broadcast_control",
"label": "选择音源",
"options": [
{"label": "晨间新闻", "value": "0"},
{"label": "背景音乐", "value": "1"},
{"label": "轻音乐", "value": "2"},
{"label": "古典音乐", "value": "3"}
],
"topic": "factory/broadcast/station"
}
// 3. 音量滑块
{
"id": "slider_volume",
"type": "ui_slider",
"group": "broadcast_control",
"label": "音量",
"min": 0,
"max": 250,
"step": 5,
"topic": "factory/broadcast/volume"
}
// 4. 状态显示
{
"id": "text_status",
"type": "ui_text",
"group": "broadcast_status",
"label": "当前状态"
}
// 5. 调度时间表显示
{
"id": "table_schedule",
"type": "ui_table",
"group": "broadcast_schedule",
"label": "定时计划"
}
┌─────────────────────────────────────┐
│ 工业广播控制面板 │
├─────────────────────────────────────┤
│ │
│ [▶ 播放] [⏸ 暂停] [⏹ 停止] │
│ │
│ 音源: [下拉选择] │
│ │
│ 音量: [────────●────────] 65 │
│ │
│ 当前状态: 播放中 │
│ 当前音源: 背景音乐 │
│ 当前音量: 65 │
│ 当前曲目: Morning Melody │
│ │
├─────────────────────────────────────┤
│ 定时计划 │
│ 06:00 工作日 ▶ 晨间新闻 音量:40 │
│ 07:30 工作日 ▶ 背景音乐 音量:60 │
│ 12:00 工作日 ▶ 轻音乐 音量:50 │
│ 17:00 工作日 ⏹ 关闭 │
│ 21:00 每天 ⏹ 关闭 │
└─────────────────────────────────────┘
{
"id": "status_listener",
"type": "mqtt in",
"topic": "factory/broadcast/status",
"server": "localhost",
"qos": 1
}

Status Processing Function

// MQTT 状态消息处理
const status = msg.payload;
// 更新仪表板显示
const statusMsg = {
payload: `播放: ${status.playing ? '🟢 播放中' : '🔴 已停止'}
音源: ${status.station_name}
音量: ${status.volume}
信号: ${status.wifi_rssi} dBm`
};
return statusMsg;
// 记录调度事件到文件和控制台
const eventLog = {
timestamp: new Date().toISOString(),
event: msg.payload.action || msg.topic,
details: msg.payload
};
// 写入日志文件
context.global.log.push(eventLog);
if (context.global.log.length > 1000) {
context.global.log.shift();
}
node.warn(`调度事件: ${JSON.stringify(eventLog)}`);
return msg;

症状: inject 节点到达预设时间但未输出消息

原因:

  • Node-RED 服务器时区设置不正确
  • 流程未部署
  • cron 表达式错误

解决方案:

  1. 检查 Node-RED 系统时区:settings.js 中的 env 设置
  2. 确认流程已部署(右上角 Deploy 按钮)
  3. 使用 */1 * * * * 测试 cron 表达式是否正常工作

症状: Dashboard UI 显示空白

原因:

  • dashboard 节点未正确配置 group 和 tab
  • MQTT 消息未到达
  • 上下文变量未初始化

解决方案:

  1. 检查 ui_group 和 ui_tab 配置
  2. 使用 debug 节点确认 MQTT 消息到达
  3. 初始化全局上下文变量

本节介绍了 Node-RED 端的调度逻辑实现:

  1. 定时触发:使用 inject 节点的 cron 表达式实现精确时间触发
  2. Dashboard 控制:创建仪表板界面,支持手动控制和状态监控
  3. 调度管理:完整的定时播放/停止流程
  4. 状态监听:实时显示广播系统的工作状态
  5. 事件记录:记录调度执行日志