Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs

Apply the Heartbeat publication and subscription to Serial interface of nRF5 SDK for Mesh. (Additional)

I accidentally clicked verified answer while trying to edit the page.

https://devzone.nordicsemi.com/f/nordic-q-a/87116/apply-the-heartbeat-publication-and-subscription-to-serial-interface-of-nrf5-sdk-for-mesh

This is the patch file:

diff --git a/nrf5_SDK_for_Mesh_v5.0.0_src/scripts/interactive_pyaci/aci/aci_cmd.py b/nrf5_SDK_for_Mesh_v5.0.0_src/scripts/interactive_pyaci/aci/aci_cmd.py
index f7f92f3b..8fa50048 100644
--- a/nrf5_SDK_for_Mesh_v5.0.0_src/scripts/interactive_pyaci/aci/aci_cmd.py
+++ b/nrf5_SDK_for_Mesh_v5.0.0_src/scripts/interactive_pyaci/aci/aci_cmd.py
@@ -968,6 +968,70 @@ class NetStateGet(CommandPacket):
         __data = bytearray()
         super(NetStateGet, self).__init__(0xAF, __data)
 
+class HBPublicationGet(CommandPacket):
+    """Gets the Heartbeat Publication state"""
+    def __init__(self):
+        __data = bytearray()
+        super(HBPublicationGet, self).__init__(0xB0, __data)
+
+class HBPublicationSet(CommandPacket):
+    """Sets the Heartbeat Publication state
+
+    Parameters
+    ----------
+        dst : uint16_t
+            The destination to send heartbeat messages.
+        count : uint32_t
+            How many messages to send.
+        period : uint32_t
+            What interval to send messages.
+        ttl : uint8_t
+            Initial TTL.
+        features : uint16_t
+            The features that trigger sending messages when changed.
+        netkey_index : uint16_t
+            The global NetKey Index of the Netkey used to send.
+
+    Return
+    ------
+        NRF_ERROR_INVALID_DATA :
+            Invalid netkey_index.
+            Check that A netkey is added corresponding the netkey_index in device
+    """
+    def __init__(self, dst, count, period, ttl, features, netkey_index):
+        __data = bytearray()
+        __data += struct.pack("<H", dst)
+        __data += struct.pack("<I", count)
+        __data += struct.pack("<I", period)
+        __data += struct.pack("<B", ttl)
+        __data += struct.pack("<H", features)
+        __data += struct.pack("<H", netkey_index)
+        super(HBPublicationSet, self).__init__(0xB1, __data)
+
+class HBSubscriptionGet(CommandPacket):
+    """Gets the Heartbeat Subscription state"""
+    def __init__(self):
+        __data = bytearray()
+        super(HBSubscriptionGet, self).__init__(0xB2, __data)
+
+class HBSubscriptionSet(CommandPacket):
+    """Sets the Heartbeat Subscription state
+
+    Parameters
+    ----------
+        src : uint16_t
+            The unicast source address for messages a node shall process.
+        dst : uint16_t
+            The destination to send heartbeat messages.
+        period : uint32_t
+            The number of seconds left for processing messages.
+    """
+    def __init__(self, src, dst, period):
+        __data = bytearray()
+        __data += struct.pack("<H", src)
+        __data += struct.pack("<H", dst)
+        __data += struct.pack("<I", period)
+        super(HBSubscriptionSet, self).__init__(0xB3, __data)
 
 class JumpToBootloader(CommandPacket):
     """Immediately jump to bootloader mode."""
@@ -1740,6 +1804,29 @@ class NetStateGetRsp(ResponsePacket):
         __data["next_seqnum_block"], = struct.unpack("<I", raw_data[7:11])
         super(NetStateGetRsp, self).__init__("NetStateGet", 0xAF, __data)
 
+class HBPublicationGetRsp(ResponsePacket):
+    """Response to a(n) HBPublicationGet command."""
+    def __init__(self, raw_data):
+        __data = {}
+        __data["dst"], = struct.unpack("<H", raw_data[0:2])
+        __data["count_log"], = struct.unpack("<B", raw_data[2:3])
+        __data["period_log"], = struct.unpack("<B", raw_data[3:4])
+        __data["ttl"], = struct.unpack("<B", raw_data[4:5])
+        __data["features"], = struct.unpack("<H", raw_data[5:7])
+        __data["netkey_index"], = struct.unpack("<H", raw_data[7:9])
+        super(HBPublicationGetRsp, self).__init__("HBPublicationGet", 0xB0, __data)
+
+class HBSubscriptionGetRsp(ResponsePacket):
+    """Response to a(n) HBSubscriptionGet command."""
+    def __init__(self, raw_data):
+        __data = {}
+        __data["src"], = struct.unpack("<H", raw_data[0:2])
+        __data["dst"], = struct.unpack("<H", raw_data[2:4])
+        __data["period_log"], = struct.unpack("<B", raw_data[4:5])
+        __data["count_log"], = struct.unpack("<B", raw_data[5:6])
+        __data["min_hops"], = struct.unpack("<B", raw_data[6:7])
+        __data["max_hops"], = struct.unpack("<B", raw_data[7:8])
+        super(HBSubscriptionGetRsp, self).__init__("HBSubscriptionGet", 0xB2, __data)
 
 class BankInfoGetRsp(ResponsePacket):
     """Response to a(n) BankInfoGet command."""
@@ -1930,6 +2017,8 @@ RESPONSE_LUT = {
     0xA6: {"object": AddrPublicationRemoveRsp, "name": "AddrPublicationRemove"},
     0xAB: {"object": PacketSendRsp, "name": "PacketSend"},
     0xAF: {"object": NetStateGetRsp, "name": "NetStateGet"},
+    0xB0: {"object": HBPublicationGetRsp, "name": "HBPublicationGet"},
+    0xB2: {"object": HBSubscriptionGetRsp, "name": "HBSubscriptionGet"},
     0xD4: {"object": BankInfoGetRsp, "name": "BankInfoGet"},
     0xD6: {"object": StateGetRsp, "name": "StateGet"},
     0xE1: {"object": ModelPubAddrGetRsp, "name": "ModelPubAddrGet"},

diff --git a/nrf5_SDK_for_Mesh_v5.0.0_src/mesh/serial/include/serial_cmd.h b/nrf5_SDK_for_Mesh_v5.0.0_src/mesh/serial/include/serial_cmd.h
index 11c94a6f..1118e6fc 100644
--- a/nrf5_SDK_for_Mesh_v5.0.0_src/mesh/serial/include/serial_cmd.h
+++ b/nrf5_SDK_for_Mesh_v5.0.0_src/mesh/serial/include/serial_cmd.h
@@ -155,6 +155,10 @@
 #define SERIAL_OPCODE_CMD_MESH_CONFIG_SERVER_BIND             (0xAD) /**< Params: @ref serial_cmd_mesh_config_server_devkey_bind_t */
 #define SERIAL_OPCODE_CMD_MESH_NET_STATE_SET                  (0xAE) /**< Params: @ref serial_cmd_mesh_net_state_set_t */
 #define SERIAL_OPCODE_CMD_MESH_NET_STATE_GET                  (0xAF) /**< Params: None. */
+#define SERIAL_OPCODE_CMD_MESH_HB_PUBLICATION_GET             (0xB0) /**< Params: None. */
+#define SERIAL_OPCODE_CMD_MESH_HB_PUBLICATION_SET             (0xB1) /**< Params: @ref serial_cmd_mesh_hb_publication_set_t */
+#define SERIAL_OPCODE_CMD_MESH_HB_SUBSCRIPTION_GET            (0xB2) /**< Params: None. */
+#define SERIAL_OPCODE_CMD_MESH_HB_SUBSCRIPTION_SET            (0xB3) /**< Params: @ref serial_cmd_mesh_hb_subscription_set_t */
 #define SERIAL_OPCODE_CMD_RANGE_MESH_END                      (0xBF) /**< MESH range end. */
 
 #define SERIAL_OPCODE_CMD_RANGE_DFU_START                     (0xD0) /**< DFU range start. */
@@ -518,6 +522,25 @@ typedef struct __attribute((packed))
     uint32_t  next_seqnum_block; /**< The first sequence number block which is not yet allocated. */
 } serial_cmd_mesh_net_state_set_t;
 
+/** Heartbeat publication set command parameters */
+typedef struct __attribute((packed))
+{
+    uint16_t  dst; /**< The destination to send heartbeat messages.*/
+    uint32_t  count; /**< How many messages to send.*/
+    uint32_t  period; /**< What interval to send messages.*/
+    uint8_t   ttl; /**< Initial TTL.*/
+    uint16_t  features; /**< The features that trigger sending messages when changed.*/
+    uint16_t  netkey_index; /**< The global NetKey Index of the Netkey used to send.*/
+} serial_cmd_mesh_hb_publication_set_t;
+
+/** Heartbeat subscription set command parameters */
+typedef struct __attribute((packed))
+{
+    uint16_t  src; /**< The unicast source address for messages a node shall process.*/
+    uint16_t  dst; /**< The destination to receive heartbeat messages.*/
+    uint32_t  period; /**< The number of seconds left for processing messages.*/
+} serial_cmd_mesh_hb_subscription_set_t;
+
 /** Mesh command parameters. */
 typedef union __attribute((packed))
 {
@@ -549,6 +572,8 @@ typedef union __attribute((packed))
     serial_cmd_mesh_packet_send_t                   packet_send;                   /**< Packet send parameters. */
     serial_cmd_mesh_config_server_devkey_bind_t     config_server_devkey_bind;     /**< Configuration Server: device key bind parameters. */
     serial_cmd_mesh_net_state_set_t                 net_state_set;                 /**< Net state set parameters */
+    serial_cmd_mesh_hb_publication_set_t            hb_publication_set;            /**< Heartbeat Publication set parameters */
+    serial_cmd_mesh_hb_subscription_set_t           hb_subscription_set;           /**< Heartbeat Subscription set parameters */
 } serial_cmd_mesh_t;
 
 /* **** PB-MESH Client **** */
diff --git a/nrf5_SDK_for_Mesh_v5.0.0_src/mesh/serial/include/serial_cmd_rsp.h b/nrf5_SDK_for_Mesh_v5.0.0_src/mesh/serial/include/serial_cmd_rsp.h
index 00f4d45b..f1733aa7 100644
--- a/nrf5_SDK_for_Mesh_v5.0.0_src/mesh/serial/include/serial_cmd_rsp.h
+++ b/nrf5_SDK_for_Mesh_v5.0.0_src/mesh/serial/include/serial_cmd_rsp.h
@@ -309,6 +309,28 @@ typedef struct __attribute((packed))
     uint32_t  next_seqnum_block; /**< The start of the next unused sequence number block. */
 } serial_evt_cmd_rsp_data_net_state_get_t;
 
+/** Command response to @ref SERIAL_OPCODE_CMD_MESH_HB_PUBLICATION_GET with the current heartbeat publication state */
+typedef struct __attribute((packed))
+{
+    uint16_t  dst; /**< The destination to send heartbeat messages.*/
+    uint8_t  count_log; /**< How many messages to send.*/
+    uint8_t  period_log; /**< What interval to send messages.*/
+    uint8_t   ttl; /**< Initial TTL.*/
+    uint16_t  features; /**< The features that trigger sending messages when changed.*/
+    uint16_t  netkey_index; /**< The global NetKey Index of the Netkey used to send.*/
+} serial_evt_cmd_rsp_data_hb_publication_get_t;
+
+/** Command response to @ref SERIAL_OPCODE_CMD_MESH_HB_SUBSCRIPTION_GET with the current heartbeat subscription state */
+typedef struct __attribute((packed))
+{
+    uint16_t  src; /**< The unicast source address for messages a node shall process.*/
+    uint16_t  dst; /**< The destination to receive heartbeat messages.*/
+    uint8_t  period_log; /**< The number of seconds left for processing messages.*/
+    uint8_t  count_log; /**< The number of periodical messages received.*/
+    uint16_t  min_hops; /**< The minimum hops value registered when receiving messages.*/
+    uint16_t  max_hops; /**< The maximum hops value registered when receiving messages.*/
+} serial_evt_cmd_rsp_data_hb_subscription_get_t;
+
 /** Command response packet. */
 typedef struct __attribute((packed))
 {
@@ -348,7 +370,8 @@ typedef struct __attribute((packed))
         serial_evt_cmd_rsp_data_model_init_t           model_init;     /**< Reserved handle for the initialized model instance. */
         serial_evt_cmd_rsp_data_packet_send_t          packet_send;    /**< Information about the sent packet. */
         serial_evt_cmd_rsp_data_net_state_get_t        net_state_get;  /**< Net state. */
-
+        serial_evt_cmd_rsp_data_hb_publication_get_t   hb_publication_get; /**< Heartbeat Publication States. */
+        serial_evt_cmd_rsp_data_hb_subscription_get_t  hb_subscription_get; /**< Heartbeat Subscription States. */
     } data; /**< Optional command response data. */
 } serial_evt_cmd_rsp_t;
 
diff --git a/nrf5_SDK_for_Mesh_v5.0.0_src/mesh/serial/src/serial_handler_mesh.c b/nrf5_SDK_for_Mesh_v5.0.0_src/mesh/serial/src/serial_handler_mesh.c
index 655cdcd3..25fd52ff 100644
--- a/nrf5_SDK_for_Mesh_v5.0.0_src/mesh/serial/src/serial_handler_mesh.c
+++ b/nrf5_SDK_for_Mesh_v5.0.0_src/mesh/serial/src/serial_handler_mesh.c
@@ -54,6 +54,7 @@
 #include "mesh_opt_net_state.h"
 #include "mesh_config_entry.h"
 #include "mesh_config_listener.h"
+#include "heartbeat.h"
 
 /* Ensure that we're mapping the size of the serial parameter to the
  * dsm_handle_t. If this triggers, someone changed the size of the
@@ -488,6 +489,143 @@ static void handle_net_state_get(const serial_packet_t * p_cmd)
     serial_handler_common_cmd_rsp_nodata_on_error(p_cmd->opcode, status, (uint8_t *)&rsp, sizeof(rsp));
 }
 
+static inline uint8_t heartbeat_pubsub_period_encode(uint32_t period)
+{
+    if (period == 0)
+    {
+        return 0x00;
+    }
+    else if (period <= HEARTBEAT_MAX_PERIOD)
+    {
+        return (log2_get(period) + 1);
+    }
+    else
+    {
+        NRF_MESH_ASSERT(false);
+        return HEARTBEAT_MAX_PERIOD_LOG;
+    }
+}
+
+static inline uint8_t heartbeat_publication_count_encode(uint32_t count)
+{
+    if (count <= 1)
+    {
+        return count;
+    }
+    else if (count <= HEARTBEAT_MAX_COUNT)
+    {
+        /* Finding smallest n where 2^(n-1) is greater than or equal to the count value */
+        return (log2_get(count - 1) + 1 + 1);
+    }
+    else if (count == HEARTBEAT_INF_COUNT)
+    {
+        return HEARTBEAT_INF_COUNT_LOG;
+    }
+    else
+    {
+        NRF_MESH_ASSERT(false);
+        return HEARTBEAT_MAX_COUNT_LOG;
+    }
+}
+
+static inline uint8_t heartbeat_subscription_count_encode(uint32_t count)
+{
+    if (count == 0)
+    {
+        return 0x00;
+    }
+    else if (count <= HEARTBEAT_MAX_COUNT)
+    {
+        /* Finding largest n where 2^(n-1) is less than or equal to the count value */
+        return (log2_get(count) + 1);
+    }
+    else if (count == HEARTBEAT_INF_COUNT)
+    {
+        return HEARTBEAT_INF_COUNT_LOG;
+    }
+    else
+    {
+        NRF_MESH_ASSERT(false);
+        return HEARTBEAT_MAX_COUNT_LOG;
+    }
+}
+
+static void handle_heartbeat_publication_get(const serial_packet_t * p_cmd)
+{
+    const heartbeat_publication_state_t * p_hb_pub = heartbeat_publication_get();
+    serial_evt_cmd_rsp_data_hb_publication_get_t rsp = {
+        .dst = p_hb_pub->dst,
+        .count_log = heartbeat_publication_count_encode(p_hb_pub->count),
+        .period_log = heartbeat_pubsub_period_encode(p_hb_pub->period),
+        .ttl = p_hb_pub->ttl,
+        .features = p_hb_pub->features,
+        .netkey_index = p_hb_pub->netkey_index
+    };
+    serial_cmd_rsp_send(p_cmd->opcode, SERIAL_STATUS_SUCCESS, (uint8_t *)&rsp, sizeof(rsp));
+}
+
+static void handle_heartbeat_publication_set(const serial_packet_t * p_cmd)
+{
+    uint32_t status;
+    const heartbeat_publication_state_t hb_pub = {
+        .dst          = p_cmd->payload.cmd.mesh.hb_publication_set.dst,
+        .count        = p_cmd->payload.cmd.mesh.hb_publication_set.count,
+        .period       = p_cmd->payload.cmd.mesh.hb_publication_set.period,
+        .ttl          = p_cmd->payload.cmd.mesh.hb_publication_set.ttl,
+        .features     = p_cmd->payload.cmd.mesh.hb_publication_set.features,
+        .netkey_index = p_cmd->payload.cmd.mesh.hb_publication_set.netkey_index
+    };
+
+    /* This is specifically required for INVALID_NETKEY status code */
+    if (dsm_net_key_index_to_subnet_handle(p_cmd->payload.cmd.mesh.hb_publication_set.netkey_index)
+                                           == DSM_HANDLE_INVALID)
+    {
+        status = NRF_ERROR_INVALID_DATA;
+    }
+    else
+    {
+        status = heartbeat_publication_set(&hb_pub);
+    }
+    serial_handler_common_cmd_rsp_nodata_on_error(p_cmd->opcode, status, NULL, 0);
+}
+static void handle_heartbeat_subscription_get(const serial_packet_t * p_cmd)
+{
+    const heartbeat_subscription_state_t * p_hb_sub = heartbeat_subscription_get();
+    serial_evt_cmd_rsp_data_hb_subscription_get_t rsp;
+
+    /* When the Heartbeat Subscription Source or Destination state is set to the unassigned address,
+     the value of - the Source and Destination fields of the Status message shall be set to the
+     unassigned address and the values of the CountLog, PeriodLog, MinHops, and MaxHops fields shall
+     be set to 0x00. Refer to @tagMeshSp section 4.4.1.2.16 */
+    if (p_hb_sub->src == NRF_MESH_ADDR_UNASSIGNED ||
+        p_hb_sub->dst == NRF_MESH_ADDR_UNASSIGNED)
+    {
+        memset(&rsp, 0, sizeof(serial_evt_cmd_rsp_data_hb_subscription_get_t));
+    }
+    else
+    {
+        rsp.src = p_hb_sub->src;
+        rsp.dst = p_hb_sub->dst;
+        rsp.count_log = heartbeat_subscription_count_encode(p_hb_sub->count);
+        rsp.period_log = heartbeat_pubsub_period_encode(p_hb_sub->period);
+        rsp.min_hops = p_hb_sub->min_hops;
+        rsp.max_hops = p_hb_sub->max_hops;
+    }
+    serial_cmd_rsp_send(p_cmd->opcode, SERIAL_STATUS_SUCCESS, (uint8_t *)&rsp, sizeof(rsp));
+}
+static void handle_heartbeat_subscription_set(const serial_packet_t * p_cmd)
+{
+    uint32_t status;
+    const heartbeat_subscription_state_t hb_sub = {
+        .src    = p_cmd->payload.cmd.mesh.hb_subscription_set.src,
+        .dst    = p_cmd->payload.cmd.mesh.hb_subscription_set.dst,
+        .period = p_cmd->payload.cmd.mesh.hb_subscription_set.period,
+        /* other state values shall remain unchanged, see @tagMeshSp section 4.4.1.2.16 */
+    };
+    status = heartbeat_subscription_set(&hb_sub);
+    serial_handler_common_cmd_rsp_nodata_on_error(p_cmd->opcode, status, NULL, 0);
+}
+
 /*****************************************************************************
 * Static functions
 *****************************************************************************/
@@ -527,7 +665,11 @@ static const mesh_serial_cmd_handler_t m_handlers[] =
     {SERIAL_OPCODE_CMD_MESH_STATE_CLEAR,                    0,                                                       0,  handle_cmd_clear},
     {SERIAL_OPCODE_CMD_MESH_CONFIG_SERVER_BIND,             sizeof(serial_cmd_mesh_config_server_devkey_bind_t),     0,  handle_config_devkey_bind},
     {SERIAL_OPCODE_CMD_MESH_NET_STATE_SET,                  sizeof(serial_cmd_mesh_net_state_set_t),                 0,  handle_net_state_set},
-    {SERIAL_OPCODE_CMD_MESH_NET_STATE_GET,                  0,                                                       0,  handle_net_state_get}
+    {SERIAL_OPCODE_CMD_MESH_NET_STATE_GET,                  0,                                                       0,  handle_net_state_get},
+    {SERIAL_OPCODE_CMD_MESH_HB_PUBLICATION_GET,             0,                                                       0,  handle_heartbeat_publication_get},
+    {SERIAL_OPCODE_CMD_MESH_HB_PUBLICATION_SET,             sizeof(serial_cmd_mesh_hb_publication_set_t),            0,  handle_heartbeat_publication_set},
+    {SERIAL_OPCODE_CMD_MESH_HB_SUBSCRIPTION_GET,            0,                                                       0,  handle_heartbeat_subscription_get},
+    {SERIAL_OPCODE_CMD_MESH_HB_SUBSCRIPTION_SET,            sizeof(serial_cmd_mesh_hb_subscription_set_t),           0,  handle_heartbeat_subscription_set}
 };
 
 static void mesh_config_listener_cb(mesh_config_change_reason_t reason, mesh_config_entry_id_t id, const void * p_entry)

This is a procedure:

$sudo python3 interactive_pyaci.py -d /dev/ttyACM0

    To control your device, use d[x], where x is the device index.
    Devices are indexed based on the order of the COM ports specified by the -d option.
    The first device, d[0], can also be accessed using device.

    Type d[x]. and hit tab to see the available methods.

Python 3.8.10 (default, Nov 26 2021, 20:14:08)
Type 'copyright', 'credits' or 'license' for more information
IPython 8.2.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: db = MeshDB("database/test_database.json")

In [2]: p = Provisioner(device, db)

In [3]: 2022-04-20 16:32:31,318 - INFO - ttyACM0: Success
2022-04-20 16:32:31,321 - INFO - ttyACM0: Success
2022-04-20 16:32:31,326 - INFO - ttyACM0: SubnetAdd: {'subnet_handle': 0}
2022-04-20 16:32:31,331 - INFO - ttyACM0: AppkeyAdd: {'appkey_handle': 0}
2022-04-20 16:32:31,336 - INFO - ttyACM0: AppkeyAdd: {'appkey_handle': 1}
In [3]:

In [3]: p.provision(uuid="0b01a6561a474762bfbaafa3f6ecb494", name="dimmer3")

In [4]: 2022-04-20 16:32:43,383 - INFO - ttyACM0: Provision: {'context': 0}
2022-04-20 16:32:43,549 - INFO - ttyACM0: Link established
2022-04-20 16:32:47,619 - INFO - ttyACM0: Received capabilities
2022-04-20 16:32:47,619 - INFO - ttyACM0: Number of elements: 3
2022-04-20 16:32:47,622 - INFO - ttyACM0: OobUse: {'context': 0}
2022-04-20 16:32:49,882 - INFO - ttyACM0: ECDH request received
2022-04-20 16:32:49,889 - INFO - ttyACM0: EcdhSecret: {'context': 0}
2022-04-20 16:32:54,346 - INFO - ttyACM0: Provisioning complete
2022-04-20 16:32:54,346 - INFO - ttyACM0:       Address(es): 0x10-0x12
2022-04-20 16:32:54,346 - INFO - ttyACM0:       Device key: 44f39b33df8a2599fdb2e942100588e7
2022-04-20 16:32:54,346 - INFO - ttyACM0:       Network key: 18eed9c2a56add85049ffc3c59ad0e12
2022-04-20 16:32:54,346 - INFO - ttyACM0: Adding device key to subnet 0
2022-04-20 16:32:54,346 - INFO - ttyACM0: Adding publication address of root element
2022-04-20 16:32:54,355 - INFO - ttyACM0: DevkeyAdd: {'devkey_handle': 8}
2022-04-20 16:32:54,356 - INFO - ttyACM0: AddrPublicationAdd: {'address_handle': 0}
2022-04-20 16:32:54,522 - INFO - ttyACM0: Provisioning link closed
In [4]:

In [4]: cc = ConfigurationClient(db)

In [5]: device.model_add(cc)

In [6]: cc.publish_set(8, 0)

In [7]: device.send(cmd.HBSubscriptionSet(16, 0xFFFF, 0xFF))

In [8]: 2022-04-20 16:33:57,661 - INFO - ttyACM0: Success
In [8]:

In [8]: device.send(cmd.HBSubscriptionGet())

In [9]: 2022-04-20 16:34:28,137 - INFO - ttyACM0: HBSubscriptionGet: {'src': 16, 'dst': 65535, 'period_log': 8, 'count_log': 0, 'min_hops': 127, 'max_hops': 0}
In [9]:

In [9]: cc.heartbeat_publication_set(0xFFFF, 0xF, 2, feature_bitfield=2, ttl=11)

In [10]: 2022-04-20 16:34:40,192 - INFO - ttyACM0: PacketSend: {'token': 1}
In [10]: 2022-04-20 16:34:40,215 - INFO - ttyACM0: {event: MeshTxComplete, data: {'token': 1}}
In [10]: cc.heartbeat_publication_set(0xFFFF, 0xF, 2, feature_bitfield=2, ttl=11)

In [11]: 2022-04-20 16:34:43,852 - INFO - ttyACM0: PacketSend: {'token': 2}
2022-04-20 16:34:43,871 - INFO - ttyACM0: {event: MeshTxComplete, data: {'token': 2}}
2022-04-20 16:34:43,917 - INFO - ttyACM0.ConfigurationClient: Heartbeat publication status: AccessStatus.SUCCESS
2022-04-20 16:34:43,917 - INFO - ttyACM0.ConfigurationClient: Heartbeat publication state: dst: ffff, count: 8, period: 2s, features: {'relay': False, 'proxy': True, 'friend': False, 'lowPower': False}, subnet: 0
2022-04-20 16:34:43,925 - INFO - ttyACM0: {event: MeshHeartbeatReceived, data: {'init_ttl': 11, 'hops': 1, 'features': 0, 'src': 16}}
In [11]:

In [11]: 2022-04-20 16:34:45,899 - INFO - ttyACM0: {event: MeshHeartbeatReceived, data: {'init_ttl': 11, 'hops': 1, 'features': 0, 'src': 16}}
2022-04-20 16:34:47,895 - INFO - ttyACM0: {event: MeshHeartbeatReceived, data: {'init_ttl': 11, 'hops': 1, 'features': 0, 'src': 16}}
2022-04-20 16:34:49,902 - INFO - ttyACM0: {event: MeshHeartbeatReceived, data: {'init_ttl': 11, 'hops': 1, 'features': 0, 'src': 16}}
2022-04-20 16:34:51,896 - INFO - ttyACM0: {event: MeshHeartbeatReceived, data: {'init_ttl': 11, 'hops': 1, 'features': 0, 'src': 16}}
2022-04-20 16:34:53,917 - INFO - ttyACM0: {event: MeshHeartbeatReceived, data: {'init_ttl': 11, 'hops': 1, 'features': 0, 'src': 16}}
2022-04-20 16:34:55,930 - INFO - ttyACM0: {event: MeshHeartbeatReceived, data: {'init_ttl': 11, 'hops': 1, 'features': 0, 'src': 16}}
2022-04-20 16:34:57,903 - INFO - ttyACM0: {event: MeshHeartbeatReceived, data: {'init_ttl': 11, 'hops': 1, 'features': 0, 'src': 16}}
In [11]:

In [11]: device.send(cmd.HBSubscriptionGet())

In [12]: 2022-04-20 16:35:06,889 - INFO - ttyACM0: HBSubscriptionGet: {'src': 16, 'dst': 65535, 'period_log': 8, 'count_log': 4, 'min_hops': 1, 'max_hops': 0}
In [12]:

In [12]: cc.heartbeat_subscription_set(1, 0xffff, 0xFF)

In [13]: 2022-04-20 16:35:36,163 - INFO - ttyACM0: PacketSend: {'token': 3}
In [13]: 2022-04-20 16:35:36,216 - INFO - ttyACM0: {event: MeshTxComplete, data: {'token': 3}}
2022-04-20 16:35:36,219 - INFO - ttyACM0.ConfigurationClient: Heartbeat subscription status: AccessStatus.SUCCESS
2022-04-20 16:35:36,219 - INFO - ttyACM0.ConfigurationClient: Heartbeat subscription state: src: 0001, dst: ffff, period: 128s, count: 0, min/max: 127/0
In [13]:

In [13]: device.send(cmd.HBPublicationSet(0xFFFF, 0xF, 2, features=2, ttl=11, netkey_index=0))

In [14]: 2022-04-20 16:35:55,190 - INFO - ttyACM0: Success
2022-04-20 16:35:55,192 - INFO - ttyACM0: {event: MeshTxComplete, data: {'token': 4294967293}}
2022-04-20 16:35:55,213 - INFO - ttyACM0: {event: MeshTxComplete, data: {'token': 4294967293}}
2022-04-20 16:35:57,192 - INFO - ttyACM0: {event: MeshTxComplete, data: {'token': 4294967293}}
2022-04-20 16:35:57,239 - INFO - ttyACM0: {event: MeshTxComplete, data: {'token': 4294967293}}
2022-04-20 16:35:59,192 - INFO - ttyACM0: {event: MeshTxComplete, data: {'token': 4294967293}}
2022-04-20 16:35:59,241 - INFO - ttyACM0: {event: MeshTxComplete, data: {'token': 4294967293}}
2022-04-20 16:36:01,192 - INFO - ttyACM0: {event: MeshTxComplete, data: {'token': 4294967293}}
2022-04-20 16:36:01,236 - INFO - ttyACM0: {event: MeshTxComplete, data: {'token': 4294967293}}
2022-04-20 16:36:03,199 - INFO - ttyACM0: {event: MeshTxComplete, data: {'token': 4294967293}}
2022-04-20 16:36:03,214 - INFO - ttyACM0: {event: MeshTxComplete, data: {'token': 4294967293}}
2022-04-20 16:36:05,192 - INFO - ttyACM0: {event: MeshTxComplete, data: {'token': 4294967293}}
2022-04-20 16:36:05,235 - INFO - ttyACM0: {event: MeshTxComplete, data: {'token': 4294967293}}
2022-04-20 16:36:07,205 - INFO - ttyACM0: {event: MeshTxComplete, data: {'token': 4294967293}}
2022-04-20 16:36:07,213 - INFO - ttyACM0: {event: MeshTxComplete, data: {'token': 4294967293}}
2022-04-20 16:36:09,192 - INFO - ttyACM0: {event: MeshTxComplete, data: {'token': 4294967293}}
2022-04-20 16:36:09,248 - INFO - ttyACM0: {event: MeshTxComplete, data: {'token': 4294967293}}
2022-04-20 16:36:11,193 - INFO - ttyACM0: {event: MeshTxComplete, data: {'token': 4294967293}}
2022-04-20 16:36:11,222 - INFO - ttyACM0: {event: MeshTxComplete, data: {'token': 4294967293}}
2022-04-20 16:36:13,213 - INFO - ttyACM0: {event: MeshTxComplete, data: {'token': 4294967293}}
2022-04-20 16:36:13,213 - INFO - ttyACM0: {event: MeshTxComplete, data: {'token': 4294967293}}
2022-04-20 16:36:15,192 - INFO - ttyACM0: {event: MeshTxComplete, data: {'token': 4294967293}}
2022-04-20 16:36:15,237 - INFO - ttyACM0: {event: MeshTxComplete, data: {'token': 4294967293}}
2022-04-20 16:36:17,195 - INFO - ttyACM0: {event: MeshTxComplete, data: {'token': 4294967293}}
2022-04-20 16:36:17,212 - INFO - ttyACM0: {event: MeshTxComplete, data: {'token': 4294967293}}
2022-04-20 16:36:19,209 - INFO - ttyACM0: {event: MeshTxComplete, data: {'token': 4294967293}}
2022-04-20 16:36:19,217 - INFO - ttyACM0: {event: MeshTxComplete, data: {'token': 4294967293}}
2022-04-20 16:36:21,216 - INFO - ttyACM0: {event: MeshTxComplete, data: {'token': 4294967293}}
2022-04-20 16:36:21,222 - INFO - ttyACM0: {event: MeshTxComplete, data: {'token': 4294967293}}
2022-04-20 16:36:23,192 - INFO - ttyACM0: {event: MeshTxComplete, data: {'token': 4294967293}}
2022-04-20 16:36:23,239 - INFO - ttyACM0: {event: MeshTxComplete, data: {'token': 4294967293}}
In [14]:

In [14]: cc.heartbeat_subscription_get()

In [15]: 2022-04-20 16:36:37,077 - INFO - ttyACM0: PacketSend: {'token': 4}
In [15]: 2022-04-20 16:36:37,104 - INFO - ttyACM0: {event: MeshTxComplete, data: {'token': 4}}
In [15]: cc.heartbeat_subscription_get()

In [16]: 2022-04-20 16:36:38,664 - INFO - ttyACM0: PacketSend: {'token': 5}
2022-04-20 16:36:38,665 - INFO - ttyACM0: {event: MeshTxComplete, data: {'token': 5}}
In [16]:

In [16]: cc.heartbeat_subscription_get()

In [17]: 2022-04-20 16:36:42,163 - INFO - ttyACM0: PacketSend: {'token': 6}
2022-04-20 16:36:42,189 - INFO - ttyACM0: {event: MeshTxComplete, data: {'token': 6}}
2022-04-20 16:36:42,226 - INFO - ttyACM0.ConfigurationClient: Heartbeat subscription status: AccessStatus.SUCCESS
2022-04-20 16:36:42,226 - INFO - ttyACM0.ConfigurationClient: Heartbeat subscription state: src: 0001, dst: ffff, period: 32s, count: 4, min/max: 1/1
In [17]:

And, I wonder there are other ways for heartbeat models in serial interface and also health model officially.

Related