What is the case
HandleHeartbeat (src/CanKit.Pro.CANopen/CanOpenNode.cs) masks bit 7 of the first byte (data[0] & 0x7F) and reports the rest. CiA 301 §7.2.8.3.2 makes that bit reserved (always 0) in a heartbeat, so a frame such as 0x84 is reported as HeartbeatReceived(Stopped) although it is not a valid heartbeat; it is accepted because the same COB-ID carries node-guarding replies, whose bit 7 is the toggle, and a node without a guarding consumer for that producer reads them as heartbeats.
The 0x80 case (state 0, which read as a boot-up) is fixed with #265. What stays is whether a heartbeat with bit 7 set should be reported at all, which also decides whether a node that sees a peer's guarding reply (answering a third party's poll) keeps reporting it as a heartbeat today.
Options
- Report only frames with bit 7 clear as
HeartbeatReceived; frames with bit 7 set still count toward liveness. A guarding reply seen by a bystander stops raising HeartbeatReceived.
- Leave it; document that the event can carry a bystander's guarding reply.
Found by Codex on #265; the behaviour predates it.
🤖 Generated with Claude Code
What is the case
HandleHeartbeat(src/CanKit.Pro.CANopen/CanOpenNode.cs) masks bit 7 of the first byte (data[0] & 0x7F) and reports the rest. CiA 301 §7.2.8.3.2 makes that bit reserved (always 0) in a heartbeat, so a frame such as0x84is reported asHeartbeatReceived(Stopped)although it is not a valid heartbeat; it is accepted because the same COB-ID carries node-guarding replies, whose bit 7 is the toggle, and a node without a guarding consumer for that producer reads them as heartbeats.The
0x80case (state 0, which read as a boot-up) is fixed with #265. What stays is whether a heartbeat with bit 7 set should be reported at all, which also decides whether a node that sees a peer's guarding reply (answering a third party's poll) keeps reporting it as a heartbeat today.Options
HeartbeatReceived; frames with bit 7 set still count toward liveness. A guarding reply seen by a bystander stops raisingHeartbeatReceived.Found by Codex on #265; the behaviour predates it.
🤖 Generated with Claude Code