node-red-contrib-symi-modbus 2.9.4 → 2.9.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -1
- package/nodes/lightweight-protocol.js +50 -4
- package/nodes/modbus-slave-switch.js +13 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -888,7 +888,23 @@ HomeKit网桥节点无需输入消息,自动同步主站配置和状态。
|
|
|
888
888
|
|
|
889
889
|
## 版本信息
|
|
890
890
|
|
|
891
|
-
**当前版本**: v2.9.
|
|
891
|
+
**当前版本**: v2.9.6 (2025-12-20)
|
|
892
|
+
|
|
893
|
+
**v2.9.6 更新内容**:
|
|
894
|
+
- **重要修复**:485开关场景按钮的CRC校验兼容性问题
|
|
895
|
+
- 修复不同按键编号的场景按钮因CRC校验值不同而被错误拒绝的问题
|
|
896
|
+
- 问题根因:不同按键编号(如按钮3、按钮4)会产生不同的CRC校验值,严格校验导致某些厂家的485开关帧被丢弃
|
|
897
|
+
- 解决方案:对于按键事件帧(SET/REPORT类型),采用宽松的CRC校验策略
|
|
898
|
+
- 宽松策略:如果CRC不匹配,但帧头、帧尾、数据长度都正确,仍然解析该帧
|
|
899
|
+
- 现在1-8路所有按键的场景按钮都能正常解析处理,兼容不同厂家的485开关
|
|
900
|
+
- 测试通过:按钮1-8全部测试通过,包括错误CRC的宽松模式测试
|
|
901
|
+
|
|
902
|
+
**v2.9.5 更新内容**:
|
|
903
|
+
- **重要修复**:重启Node-RED时设备自动动作问题
|
|
904
|
+
- 修复重启后继电器会自动动作一次的bug
|
|
905
|
+
- 问题根因:首次轮询(source='init')时modbus-slave-switch向下游发送状态消息,触发下游节点执行控制命令
|
|
906
|
+
- 解决方案:首次轮询时只同步内部状态和LED反馈,不发送消息到下游节点
|
|
907
|
+
- 现在重启Node-RED不会导致任何继电器动作,只会同步指示灯状态
|
|
892
908
|
|
|
893
909
|
**v2.9.4 更新内容**:
|
|
894
910
|
- **重要修复**:场景按钮LED反馈不同步问题
|
|
@@ -202,6 +202,7 @@ module.exports = {
|
|
|
202
202
|
|
|
203
203
|
/**
|
|
204
204
|
* 解析接收到的协议帧(支持粘包处理)
|
|
205
|
+
* 修复:对于场景按钮,采用宽松的CRC校验策略,兼容不同厂家的485开关
|
|
205
206
|
* @param {Buffer} buffer - 接收到的数据
|
|
206
207
|
* @returns {Object|null} 解析结果或null
|
|
207
208
|
*/
|
|
@@ -252,8 +253,35 @@ module.exports = {
|
|
|
252
253
|
// 验证CRC8校验
|
|
253
254
|
const receivedCRC = frameBuffer[frameBuffer.length - 2];
|
|
254
255
|
const calculatedCRC = this.calculateCRC8(frameBuffer, frameBuffer.length);
|
|
256
|
+
|
|
257
|
+
// 宽松校验策略:对于SET类型的帧(按键事件),即使CRC不匹配也尝试解析
|
|
258
|
+
// 这是为了兼容不同厂家的485开关,某些开关的CRC计算方式可能略有差异
|
|
259
|
+
const dataType = frameBuffer[2];
|
|
260
|
+
const deviceType = frameBuffer[4];
|
|
261
|
+
const opCode = frameBuffer.length > 11 ? frameBuffer[11] : 0;
|
|
262
|
+
const opInfo = frameBuffer.length > 12 ? frameBuffer[12] : 0;
|
|
263
|
+
|
|
264
|
+
// 判断是否是按键事件帧:
|
|
265
|
+
// 1. 数据类型为SET(0x03)或REPORT(0x04)
|
|
266
|
+
// 2. 设备类型为灯光(0x01)或场景(0x07)
|
|
267
|
+
// 3. 操作码为单灯控制(0x00)
|
|
268
|
+
const isButtonEvent = (dataType === 0x03 || dataType === 0x04) &&
|
|
269
|
+
(deviceType === 0x01 || deviceType === 0x07) &&
|
|
270
|
+
opCode === 0x00;
|
|
271
|
+
|
|
255
272
|
if (receivedCRC !== calculatedCRC) {
|
|
256
|
-
|
|
273
|
+
// CRC校验失败
|
|
274
|
+
// 如果是按键事件帧,且帧头、帧尾、数据长度都正确,则采用宽松策略继续解析
|
|
275
|
+
if (isButtonEvent &&
|
|
276
|
+
frameBuffer[0] === this.FRAME_HEADER &&
|
|
277
|
+
frameBuffer[frameBuffer.length - 1] === this.FRAME_TAIL &&
|
|
278
|
+
frameBuffer[3] === dataLen) {
|
|
279
|
+
// 宽松模式:允许CRC不匹配的按键事件帧通过
|
|
280
|
+
// 这是为了兼容不同厂家的485开关,某些开关的CRC计算方式可能略有差异
|
|
281
|
+
// 日志记录:CRC不匹配但仍然解析(方便调试)
|
|
282
|
+
} else {
|
|
283
|
+
return null; // 非按键事件,严格校验CRC
|
|
284
|
+
}
|
|
257
285
|
}
|
|
258
286
|
|
|
259
287
|
// 解析数据
|
|
@@ -340,12 +368,30 @@ module.exports = {
|
|
|
340
368
|
continue;
|
|
341
369
|
}
|
|
342
370
|
|
|
343
|
-
// 验证CRC
|
|
371
|
+
// 验证CRC(采用宽松策略)
|
|
344
372
|
const receivedCRC = frameBuffer[frameBuffer.length - 2];
|
|
345
373
|
const calculatedCRC = this.calculateCRC8(frameBuffer, frameBuffer.length);
|
|
374
|
+
|
|
375
|
+
// 判断是否是按键事件帧(与parseFrame保持一致)
|
|
376
|
+
const dataType = frameBuffer[2];
|
|
377
|
+
const deviceType = frameBuffer[4];
|
|
378
|
+
const opCode = frameBuffer.length > 11 ? frameBuffer[11] : 0;
|
|
379
|
+
const isButtonEvent = (dataType === 0x03 || dataType === 0x04) &&
|
|
380
|
+
(deviceType === 0x01 || deviceType === 0x07) &&
|
|
381
|
+
opCode === 0x00;
|
|
382
|
+
|
|
346
383
|
if (receivedCRC !== calculatedCRC) {
|
|
347
|
-
|
|
348
|
-
|
|
384
|
+
// CRC校验失败
|
|
385
|
+
// 如果是按键事件帧,且帧头、帧尾、数据长度都正确,则采用宽松策略继续解析
|
|
386
|
+
if (isButtonEvent &&
|
|
387
|
+
frameBuffer[0] === this.FRAME_HEADER &&
|
|
388
|
+
frameBuffer[frameBuffer.length - 1] === this.FRAME_TAIL &&
|
|
389
|
+
frameBuffer[3] === dataLen) {
|
|
390
|
+
// 宽松模式:允许CRC不匹配的按键事件帧通过
|
|
391
|
+
} else {
|
|
392
|
+
offset = startIndex + 1; // 非按键事件,CRC错误,跳过继续搜索
|
|
393
|
+
continue;
|
|
394
|
+
}
|
|
349
395
|
}
|
|
350
396
|
|
|
351
397
|
// 解析帧
|
|
@@ -730,8 +730,10 @@ module.exports = function(RED) {
|
|
|
730
730
|
// 检查状态是否真正变化
|
|
731
731
|
const stateChanged = (node.currentState !== value);
|
|
732
732
|
|
|
733
|
-
//
|
|
733
|
+
// 区分触发源
|
|
734
734
|
const isButtonPress = (triggerSource === 'button-press' || triggerSource === 'scene-trigger');
|
|
735
|
+
const isInit = (data.source === 'init'); // 首次轮询
|
|
736
|
+
const isPolling = (data.source === 'polling'); // 轮询变化
|
|
735
737
|
|
|
736
738
|
// 更新当前状态
|
|
737
739
|
node.currentState = value;
|
|
@@ -747,14 +749,22 @@ module.exports = function(RED) {
|
|
|
747
749
|
// 更新节点状态显示
|
|
748
750
|
node.updateStatus();
|
|
749
751
|
|
|
750
|
-
//
|
|
752
|
+
// 首次轮询时只同步内部状态和LED反馈,不发送消息到下游
|
|
753
|
+
// 避免重启Node-RED时触发下游节点导致继电器动作
|
|
754
|
+
if (isInit) {
|
|
755
|
+
node.debug(`首次轮询状态同步:从站${slave} 线圈${coil} = ${value ? 'ON' : 'OFF'}(不发送到下游)`);
|
|
756
|
+
return;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
// 输出状态消息(仅在非首次轮询时发送)
|
|
751
760
|
node.send({
|
|
752
761
|
payload: value,
|
|
753
762
|
topic: `switch_${node.config.switchId}_btn${node.config.buttonNumber}`,
|
|
754
763
|
switchId: node.config.switchId,
|
|
755
764
|
button: node.config.buttonNumber,
|
|
756
765
|
targetSlave: node.config.targetSlaveAddress,
|
|
757
|
-
targetCoil: node.config.targetCoilNumber
|
|
766
|
+
targetCoil: node.config.targetCoilNumber,
|
|
767
|
+
source: isPolling ? 'polling' : triggerSource // 传递触发源
|
|
758
768
|
});
|
|
759
769
|
}
|
|
760
770
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-red-contrib-symi-modbus",
|
|
3
|
-
"version": "2.9.
|
|
3
|
+
"version": "2.9.6",
|
|
4
4
|
"description": "Node-RED Modbus节点,支持TCP/串口通信、串口自动搜索、多设备轮询、可选MQTT集成(支持纯本地模式和MQTT模式)、Home Assistant自动发现、HomeKit网桥、可视化控制看板、自定义协议转换和物理开关面板双向同步,工控机长期稳定运行",
|
|
5
5
|
"main": "nodes/modbus-master.js",
|
|
6
6
|
"scripts": {
|