开始之前
- 已启用 Node-RED。Cerbo GX 运行 Venus OS Large,并已启用 Node-RED;编辑器地址为
https://venus.local:1881。操作方法:首次启动 Node-RED。 - GX 中可看到电池监控器。SoC 来自连接到 GX 的电池监控器或 BMS,例如 SmartShunt、BMV 或 Lynx Shunt。如果 GX 不显示荷电状态,Node-RED 也无法获取。
- 将继电器设为手动模式。按 Victron 要求,继电器只有切换为手动控制后,才能通过 Node-RED 控制。对于 Cerbo GX 继电器:进入 Settings → Integrations → Relays,将功能设为“Manual”。同一菜单中还可设置继电器极性(常开或常闭)。

其他继电器也可通过 Node-RED 控制,但都需要先修改设置:BMV 的继电器选项设为 REM(在 VictronConnect 中);Lynx Shunt VE.Can 的功能设为“Manual control”(在 GX 菜单中);较大型号的 MPPT 控制器和 RS 系列设备设为手动模式(在 VictronConnect 中)。Lynx Smart BMS 的继电器无法通过 Node-RED 控制。MultiPlus 和 Quattro 的报警继电器及 AC-out 2 接触器也无法控制。
您想启动发电机吗?GX 内置通过继电器自动启动和停止发电机的功能,可单独设置启动条件(见 GX 手册的“Generator auto start/stop”章节),无需 Node-RED。下方的 Victron 发电机示例并不直接控制继电器,而是使用 Generator 节点。
此流程的工作原理
- Battery(输入)读取电池监控器的荷电状态,每次变化时发送;未勾选“only changes”时,还会每 5 s 发送一次。
- 滞回(function)SoC ≤ 30% → 1,SoC ≥ 50% → 0,两者之间保持不变。仅在状态变化时发送。
- Relay(输出)1 使 Cerbo GX 的继电器 1 闭合,0 使其断开(/Relay/0/State)。
- debug在 Debug 面板中显示发送给继电器的每个数值。
逐步搭建
- 新建标签页。在 Node-RED 编辑器中添加新的流程标签页(工作区上方的“+”按钮),避免此示例与其他流程混在一起。
- Battery 节点。从节点面板的 Victron 分类中拖入 Battery 输入节点。在设置中选择您的电池监控器以及“State of charge (%)”测量值。不要勾选“only changes”——这样节点会在 SoC 每次变化时发送,并额外每 5 秒发送一次,因此重启后函数也能很快收到数值。
- function 节点。添加普通的 function 节点,将其命名为“滞回”,并粘贴“滞回代码”部分中的代码。将 Battery 节点的输出连接到函数的输入。
- Relay 节点。添加 Relay 输出节点,选择“Venus device”和“Venus relay 1 state”(路径为
/Relay/0/State——路径中的编号从 0 开始,因此索引 0 对应继电器 1)。节点根据收到的msg.payload设置状态:0 — Open(断开),1 — Closed(闭合)。将函数的输出连接到此节点。 - debug 节点。将函数的输出同时连接到 debug 节点。您可以在编辑器右侧的 Debug 面板中查看数值,在“滞回”节点下方查看当前 SoC 和继电器状态。
- 部署与测试。点击 Deploy。为避免等待电池放电,可临时添加 inject 节点,设置数值(类型为“number”,例如先输入 25,再输入 55),连接到函数,并检查 Relays 菜单中的继电器是否切换。随后删除 inject,再次点击 Deploy。
滞回代码
如果继电器在同一个阈值(例如 40%)下接通和断开,那么当 SoC 在此数值附近波动时,继电器就会反复切换。滞回将这两个阈值分开:SoC 为 30% 或更低时继电器闭合,只有达到 50% 或更高时才断开。两者之间保持原有状态。函数将此状态保存在节点上下文中,仅在状态变化时发送消息,因此不会每 5 秒向继电器重复发送相同数值。
// 滞回:SoC <= 30 % 时闭合继电器,SoC >= 50 % 时断开。
// 阈值仅为示例,请自行设置。
const PROG_ZAL = 30;
const PROG_WYL = 50;
const soc = msg.payload;
if (typeof soc !== 'number' || isNaN(soc)) return null; // 无读数:不执行任何操作
const poprzedni = context.get('stan');
let stan = (poprzedni === undefined) ? 0 : poprzedni;
if (soc <= PROG_ZAL) stan = 1; // 1 = Closed(闭合)
else if (soc >= PROG_WYL) stan = 0; // 0 = Open(断开)
// 两个阈值之间保持状态不变
node.status({fill: stan ? 'green' : 'grey', shape: 'dot',
text: 'SoC ' + soc.toFixed(1) + ' %,继电器' + (stan ? '闭合' : '断开')});
if (stan === poprzedni) return null; // 仅在状态变化时发送
context.set('stan', stan);
msg.payload = stan;
return msg;
| SoC | 函数操作 | 继电器 |
|---|---|---|
| 60% | 首次读数,50% 或更高 → 发送 0 | 断开 |
| 40% | 两个阈值之间——不发送 | 断开 |
| 30% | 30% 或更低 → 发送 1 | 闭合 |
| 45% | 两个阈值之间——不发送 | 闭合 |
| 50% | 50% 或更高 → 发送 0 | 断开 |
- 反向逻辑。您想在 SoC 较低时断开负载,而不是接通负载吗?将代码中包含阈值的两行里的 1 和 0 互换,或将电路连接到继电器的另一触点——用 NC 代替 NO。
- 无读数。如果
msg.payload不是数值(例如电池监控器已断开连接),函数不会发送任何消息,继电器保持原有状态。 - 重启后。节点上下文默认保存在 GX 设备的 RAM 中,重启或断电时会丢失。重启后的首次读数始终会设置继电器状态:如果 SoC 位于两个阈值之间,则设置为 0(断开)——即使重启前处于闭合状态也是如此。如何保留上下文,请参阅“注意事项”部分。
可直接导入的流程
以下是同一流程的 JSON 格式。Victron 节点的构建方式与 node-red-contrib-victron 节点库文档中的示例一致。
- 复制下方整个代码块,包括括号
[和]。 - 导入。在编辑器中打开菜单(右上角按钮)→ Import,或按 Ctrl-I,粘贴文本并点击 Import。流程将出现在新的“按 SoC 控制继电器”标签页中。
- 选择您的电池监控器。打开“电池 SoC”节点,从列表中选择您的电池监控器以及“State of charge (%)”。
- 在“GX 继电器 1”节点中检查继电器,根据需要修改函数中的阈值,然后点击 Deploy。
Battery 节点指向 Victron 文档中的示例 SmartShunt(com.victronenergy.battery/277)。末尾编号因系统而异,取决于设备及连接方式。在节点中选择您自己的电池监控器之前,函数不会收到 SoC,继电器也不会切换。
[
{"id":"c24a5c0f10000001","type":"tab","label":"按 SoC 控制继电器","disabled":false,"info":"30/50 % 滞回控制 - cpv24.pl 示例"},
{"id":"c24a5c0f10000002","type":"victron-input-battery","z":"c24a5c0f10000001","service":"com.victronenergy.battery/277","path":"/Soc","serviceObj":{"service":"com.victronenergy.battery/277","name":"SmartShunt(示例 - 请选择您的设备)"},"pathObj":{"path":"/Soc","type":"float","name":"State of charge (%)"},"initial":"","name":"电池 SoC","onlyChanges":false,"x":150,"y":120,"wires":[["c24a5c0f10000003"]]},
{"id":"c24a5c0f10000003","type":"function","z":"c24a5c0f10000001","name":"滞回控制","func":"// 滞回控制:SoC <= 30 % 时闭合继电器,SoC >= 50 % 时断开。\n// 阈值仅为示例 - 请设置您自己的阈值。\nconst PROG_ZAL = 30;\nconst PROG_WYL = 50;\n\nconst soc = msg.payload;\nif (typeof soc !== 'number' || isNaN(soc)) return null; // 无读数:不执行操作\n\nconst poprzedni = context.get('stan');\nlet stan = (poprzedni === undefined) ? 0 : poprzedni;\nif (soc <= PROG_ZAL) stan = 1; // 1 = Closed(闭合)\nelse if (soc >= PROG_WYL) stan = 0; // 0 = Open(断开)\n// 两个阈值之间保持状态不变\n\nnode.status({fill: stan ? 'green' : 'grey', shape: 'dot',\n text: 'SoC ' + soc.toFixed(1) + ' %,继电器' + (stan ? '闭合' : '断开')});\n\nif (stan === poprzedni) return null; // 仅在状态变化时发送\ncontext.set('stan', stan);\nmsg.payload = stan;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":370,"y":120,"wires":[["c24a5c0f10000004","c24a5c0f10000005"]]},
{"id":"c24a5c0f10000004","type":"victron-output-relay","z":"c24a5c0f10000001","service":"com.victronenergy.system/0","path":"/Relay/0/State","serviceObj":{"service":"com.victronenergy.system/0","name":"Venus device"},"pathObj":{"path":"/Relay/0/State","type":"enum","name":"Venus relay 1 state","enum":{"0":"Open","1":"Closed"},"writable":true},"initial":"","name":"GX 继电器 1","onlyChanges":true,"x":600,"y":120,"wires":[]},
{"id":"c24a5c0f10000005","type":"debug","z":"c24a5c0f10000001","name":"继电器状态","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":610,"y":180,"wires":[]}
]
Victron 内置示例
Victron 节点面板内置示例库:菜单(右上角)→ Import → Examples → node-red-contrib-victron。同样的流程也可在节点面板的 wiki 页面查看,无需打开编辑器。其中三个示例与本页示例最接近:
| 示例 | 功能 | 与本页流程的区别 |
|---|---|---|
| Threshold Control | 输入值超过 ON 阈值时开启输出,低于 OFF 阈值时关闭输出。每次切换前都会等待设定的延迟时间(ON/OFF delay)。阈值和手动控制位于仪表板上。 | 控制同一个继电器 1(/Relay/0/State),但输入是 System 节点的电池电压(/Dc/Battery/Voltage),而非 SoC,并增加了延迟。需要额外模块:node-red-dashboard、node-red-contrib-ui-led 和 node-red-contrib-countdown。 |
| Run generator at certain time when SOC is below 50% | 每天 18.00 检查已保存的 SoC。如果电量较低,则启动发电机运行 45 分钟:trigger 节点先发送 1,45 分钟后发送 0。SoC 通过 change 节点保存到上下文 flow.SOC。 | 控制 Generator 节点(Manual Start),而非继电器。注意:说明写的是“低于 50%”,但该流程中的 switch 节点检查的是“小于或等于 50”(名称为“Start if SOC is <= 50%”)。 |
| Only messages at regular intervals | change 节点将最新值保存到上下文,inject 节点按固定间隔读取该值。 | 当您需要按固定周期获取最新值,而不受输入节点发送时间影响时,此流程很有用。 |
所有 Victron 输入和输出节点的说明:Node-RED 中的 Victron 节点。
注意事项
- 重启后的上下文。要在重启后保留上下文,可参考 Victron FAQ 中的说明,在
/data/home/nodered/.node-red/settings-user.js文件中设置contextStorage(使用localfilesystem模块,每 300 s,即每 5 分钟保存一次)。如果将其设为默认上下文存储,函数代码无需修改即可运行。Victron 提醒不要过于频繁地写入,以免造成闪存磨损。 - 安全模式。如果修改后的流程导致 GX 负荷过高或运行异常,请在 GX 菜单中以安全模式(safe mode)启用 Node-RED。Node-RED 将启动,但不运行流程,您可以修改流程,点击 Deploy 后才会运行。
- 重启 Node-RED。在 GX 菜单的 Venus OS Large Features → Node-RED 中,关闭后重新开启 Node-RED。
- 流程中的时间。在按时间控制的示例中,例如 18.00 启动发电机,Node-RED 使用 Remote Console 中设置的时区。更改时区后,需要重启 Node-RED。
Node-RED 流程不能替代 BMS、保险丝或断路器——GX 重启或流程出错时,继电器可能停留在非预期状态。连接到继电器的电路必须在两种状态下都安全。Victron 不为使用 Node-RED 实现高级功能的系统提供技术支持。
适用设备
资料来源
- Venus OS Large image: Signal K and Node-RED — 第 2、5.4 和 5.7 节,FAQ Q4、Q5、Q9 和 Q11victronenergy.com · EN
- Cerbo GX 说明书 — 配置,Settings → Integrations → Relaysvictronenergy.com · EN
- node-red-contrib-victron — Input nodes(Battery,/Soc)github.com · EN
- node-red-contrib-victron — Output nodes(Relay,/Relay/{relay}/State)github.com · EN
- node-red-contrib-victron — Example Flowsgithub.com · EN
继续阅读:Cerbo GX 上的 Node-RED — 所有文章 · 首次启动 · Victron 节点 · Cerbo GX
