<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="https://devzone.nordicsemi.com/cfs-file/__key/system/syndication/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Apply the Heartbeat publication and subscription to Serial interface of nRF5 SDK for Mesh.</title><link>https://devzone.nordicsemi.com/f/nordic-q-a/87116/apply-the-heartbeat-publication-and-subscription-to-serial-interface-of-nrf5-sdk-for-mesh</link><description>In our cases, Serial interface needs to support Heartbeat of Configuration Server. 
 But configuration server&amp;#39;s heartbeat handler is not support serial interface and serial interface has no method using heartbeat server. 
 Therefore, I made it possible</description><dc:language>en-US</dc:language><generator>Telligent Community 13</generator><lastBuildDate>Wed, 20 Apr 2022 07:08:35 GMT</lastBuildDate><atom:link rel="self" type="application/rss+xml" href="https://devzone.nordicsemi.com/f/nordic-q-a/87116/apply-the-heartbeat-publication-and-subscription-to-serial-interface-of-nrf5-sdk-for-mesh" /><item><title>RE: Apply the Heartbeat publication and subscription to Serial interface of nRF5 SDK for Mesh.</title><link>https://devzone.nordicsemi.com/thread/363758?ContentTypeID=1</link><pubDate>Wed, 20 Apr 2022 07:08:35 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:c233da8c-ab7f-46e4-9a9b-a580b95cdee8</guid><dc:creator>TOT0Ro</dc:creator><description>&lt;p&gt;The post has some problem of code format, so uploaded again.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;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):
+    &amp;quot;&amp;quot;&amp;quot;Gets the Heartbeat Publication state&amp;quot;&amp;quot;&amp;quot;
+    def __init__(self):
+        __data = bytearray()
+        super(HBPublicationGet, self).__init__(0xB0, __data)
+
+class HBPublicationSet(CommandPacket):
+    &amp;quot;&amp;quot;&amp;quot;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
+    &amp;quot;&amp;quot;&amp;quot;
+    def __init__(self, dst, count, period, ttl, features, netkey_index):
+        __data = bytearray()
+        __data += struct.pack(&amp;quot;&amp;lt;H&amp;quot;, dst)
+        __data += struct.pack(&amp;quot;&amp;lt;I&amp;quot;, count)
+        __data += struct.pack(&amp;quot;&amp;lt;I&amp;quot;, period)
+        __data += struct.pack(&amp;quot;&amp;lt;B&amp;quot;, ttl)
+        __data += struct.pack(&amp;quot;&amp;lt;H&amp;quot;, features)
+        __data += struct.pack(&amp;quot;&amp;lt;H&amp;quot;, netkey_index)
+        super(HBPublicationSet, self).__init__(0xB1, __data)
+
+class HBSubscriptionGet(CommandPacket):
+    &amp;quot;&amp;quot;&amp;quot;Gets the Heartbeat Subscription state&amp;quot;&amp;quot;&amp;quot;
+    def __init__(self):
+        __data = bytearray()
+        super(HBSubscriptionGet, self).__init__(0xB2, __data)
+
+class HBSubscriptionSet(CommandPacket):
+    &amp;quot;&amp;quot;&amp;quot;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.
+    &amp;quot;&amp;quot;&amp;quot;
+    def __init__(self, src, dst, period):
+        __data = bytearray()
+        __data += struct.pack(&amp;quot;&amp;lt;H&amp;quot;, src)
+        __data += struct.pack(&amp;quot;&amp;lt;H&amp;quot;, dst)
+        __data += struct.pack(&amp;quot;&amp;lt;I&amp;quot;, period)
+        super(HBSubscriptionSet, self).__init__(0xB3, __data)
 
 class JumpToBootloader(CommandPacket):
     &amp;quot;&amp;quot;&amp;quot;Immediately jump to bootloader mode.&amp;quot;&amp;quot;&amp;quot;
@@ -1740,6 +1804,29 @@ class NetStateGetRsp(ResponsePacket):
         __data[&amp;quot;next_seqnum_block&amp;quot;], = struct.unpack(&amp;quot;&amp;lt;I&amp;quot;, raw_data[7:11])
         super(NetStateGetRsp, self).__init__(&amp;quot;NetStateGet&amp;quot;, 0xAF, __data)
 
+class HBPublicationGetRsp(ResponsePacket):
+    &amp;quot;&amp;quot;&amp;quot;Response to a(n) HBPublicationGet command.&amp;quot;&amp;quot;&amp;quot;
+    def __init__(self, raw_data):
+        __data = {}
+        __data[&amp;quot;dst&amp;quot;], = struct.unpack(&amp;quot;&amp;lt;H&amp;quot;, raw_data[0:2])
+        __data[&amp;quot;count_log&amp;quot;], = struct.unpack(&amp;quot;&amp;lt;B&amp;quot;, raw_data[2:3])
+        __data[&amp;quot;period_log&amp;quot;], = struct.unpack(&amp;quot;&amp;lt;B&amp;quot;, raw_data[3:4])
+        __data[&amp;quot;ttl&amp;quot;], = struct.unpack(&amp;quot;&amp;lt;B&amp;quot;, raw_data[4:5])
+        __data[&amp;quot;features&amp;quot;], = struct.unpack(&amp;quot;&amp;lt;H&amp;quot;, raw_data[5:7])
+        __data[&amp;quot;netkey_index&amp;quot;], = struct.unpack(&amp;quot;&amp;lt;H&amp;quot;, raw_data[7:9])
+        super(HBPublicationGetRsp, self).__init__(&amp;quot;HBPublicationGet&amp;quot;, 0xB0, __data)
+
+class HBSubscriptionGetRsp(ResponsePacket):
+    &amp;quot;&amp;quot;&amp;quot;Response to a(n) HBSubscriptionGet command.&amp;quot;&amp;quot;&amp;quot;
+    def __init__(self, raw_data):
+        __data = {}
+        __data[&amp;quot;src&amp;quot;], = struct.unpack(&amp;quot;&amp;lt;H&amp;quot;, raw_data[0:2])
+        __data[&amp;quot;dst&amp;quot;], = struct.unpack(&amp;quot;&amp;lt;H&amp;quot;, raw_data[2:4])
+        __data[&amp;quot;period_log&amp;quot;], = struct.unpack(&amp;quot;&amp;lt;B&amp;quot;, raw_data[4:5])
+        __data[&amp;quot;count_log&amp;quot;], = struct.unpack(&amp;quot;&amp;lt;B&amp;quot;, raw_data[5:6])
+        __data[&amp;quot;min_hops&amp;quot;], = struct.unpack(&amp;quot;&amp;lt;B&amp;quot;, raw_data[6:7])
+        __data[&amp;quot;max_hops&amp;quot;], = struct.unpack(&amp;quot;&amp;lt;B&amp;quot;, raw_data[7:8])
+        super(HBSubscriptionGetRsp, self).__init__(&amp;quot;HBSubscriptionGet&amp;quot;, 0xB2, __data)
 
 class BankInfoGetRsp(ResponsePacket):
     &amp;quot;&amp;quot;&amp;quot;Response to a(n) BankInfoGet command.&amp;quot;&amp;quot;&amp;quot;
@@ -1930,6 +2017,8 @@ RESPONSE_LUT = {
     0xA6: {&amp;quot;object&amp;quot;: AddrPublicationRemoveRsp, &amp;quot;name&amp;quot;: &amp;quot;AddrPublicationRemove&amp;quot;},
     0xAB: {&amp;quot;object&amp;quot;: PacketSendRsp, &amp;quot;name&amp;quot;: &amp;quot;PacketSend&amp;quot;},
     0xAF: {&amp;quot;object&amp;quot;: NetStateGetRsp, &amp;quot;name&amp;quot;: &amp;quot;NetStateGet&amp;quot;},
+    0xB0: {&amp;quot;object&amp;quot;: HBPublicationGetRsp, &amp;quot;name&amp;quot;: &amp;quot;HBPublicationGet&amp;quot;},
+    0xB2: {&amp;quot;object&amp;quot;: HBSubscriptionGetRsp, &amp;quot;name&amp;quot;: &amp;quot;HBSubscriptionGet&amp;quot;},
     0xD4: {&amp;quot;object&amp;quot;: BankInfoGetRsp, &amp;quot;name&amp;quot;: &amp;quot;BankInfoGet&amp;quot;},
     0xD6: {&amp;quot;object&amp;quot;: StateGetRsp, &amp;quot;name&amp;quot;: &amp;quot;StateGet&amp;quot;},
     0xE1: {&amp;quot;object&amp;quot;: ModelPubAddrGetRsp, &amp;quot;name&amp;quot;: &amp;quot;ModelPubAddrGet&amp;quot;},

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) /**&amp;lt; Params: @ref serial_cmd_mesh_config_server_devkey_bind_t */
 #define SERIAL_OPCODE_CMD_MESH_NET_STATE_SET                  (0xAE) /**&amp;lt; Params: @ref serial_cmd_mesh_net_state_set_t */
 #define SERIAL_OPCODE_CMD_MESH_NET_STATE_GET                  (0xAF) /**&amp;lt; Params: None. */
+#define SERIAL_OPCODE_CMD_MESH_HB_PUBLICATION_GET             (0xB0) /**&amp;lt; Params: None. */
+#define SERIAL_OPCODE_CMD_MESH_HB_PUBLICATION_SET             (0xB1) /**&amp;lt; Params: @ref serial_cmd_mesh_hb_publication_set_t */
+#define SERIAL_OPCODE_CMD_MESH_HB_SUBSCRIPTION_GET            (0xB2) /**&amp;lt; Params: None. */
+#define SERIAL_OPCODE_CMD_MESH_HB_SUBSCRIPTION_SET            (0xB3) /**&amp;lt; Params: @ref serial_cmd_mesh_hb_subscription_set_t */
 #define SERIAL_OPCODE_CMD_RANGE_MESH_END                      (0xBF) /**&amp;lt; MESH range end. */
 
 #define SERIAL_OPCODE_CMD_RANGE_DFU_START                     (0xD0) /**&amp;lt; DFU range start. */
@@ -518,6 +522,25 @@ typedef struct __attribute((packed))
     uint32_t  next_seqnum_block; /**&amp;lt; 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; /**&amp;lt; The destination to send heartbeat messages.*/
+    uint32_t  count; /**&amp;lt; How many messages to send.*/
+    uint32_t  period; /**&amp;lt; What interval to send messages.*/
+    uint8_t   ttl; /**&amp;lt; Initial TTL.*/
+    uint16_t  features; /**&amp;lt; The features that trigger sending messages when changed.*/
+    uint16_t  netkey_index; /**&amp;lt; 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; /**&amp;lt; The unicast source address for messages a node shall process.*/
+    uint16_t  dst; /**&amp;lt; The destination to receive heartbeat messages.*/
+    uint32_t  period; /**&amp;lt; 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;                   /**&amp;lt; Packet send parameters. */
     serial_cmd_mesh_config_server_devkey_bind_t     config_server_devkey_bind;     /**&amp;lt; Configuration Server: device key bind parameters. */
     serial_cmd_mesh_net_state_set_t                 net_state_set;                 /**&amp;lt; Net state set parameters */
+    serial_cmd_mesh_hb_publication_set_t            hb_publication_set;            /**&amp;lt; Heartbeat Publication set parameters */
+    serial_cmd_mesh_hb_subscription_set_t           hb_subscription_set;           /**&amp;lt; 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; /**&amp;lt; 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; /**&amp;lt; The destination to send heartbeat messages.*/
+    uint8_t  count_log; /**&amp;lt; How many messages to send.*/
+    uint8_t  period_log; /**&amp;lt; What interval to send messages.*/
+    uint8_t   ttl; /**&amp;lt; Initial TTL.*/
+    uint16_t  features; /**&amp;lt; The features that trigger sending messages when changed.*/
+    uint16_t  netkey_index; /**&amp;lt; 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; /**&amp;lt; The unicast source address for messages a node shall process.*/
+    uint16_t  dst; /**&amp;lt; The destination to receive heartbeat messages.*/
+    uint8_t  period_log; /**&amp;lt; The number of seconds left for processing messages.*/
+    uint8_t  count_log; /**&amp;lt; The number of periodical messages received.*/
+    uint16_t  min_hops; /**&amp;lt; The minimum hops value registered when receiving messages.*/
+    uint16_t  max_hops; /**&amp;lt; 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;     /**&amp;lt; Reserved handle for the initialized model instance. */
         serial_evt_cmd_rsp_data_packet_send_t          packet_send;    /**&amp;lt; Information about the sent packet. */
         serial_evt_cmd_rsp_data_net_state_get_t        net_state_get;  /**&amp;lt; Net state. */
-
+        serial_evt_cmd_rsp_data_hb_publication_get_t   hb_publication_get; /**&amp;lt; Heartbeat Publication States. */
+        serial_evt_cmd_rsp_data_hb_subscription_get_t  hb_subscription_get; /**&amp;lt; Heartbeat Subscription States. */
     } data; /**&amp;lt; 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 &amp;quot;mesh_opt_net_state.h&amp;quot;
 #include &amp;quot;mesh_config_entry.h&amp;quot;
 #include &amp;quot;mesh_config_listener.h&amp;quot;
+#include &amp;quot;heartbeat.h&amp;quot;
 
 /* Ensure that we&amp;#39;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-&amp;gt;opcode, status, (uint8_t *)&amp;amp;rsp, sizeof(rsp));
 }
 
+static inline uint8_t heartbeat_pubsub_period_encode(uint32_t period)
+{
+    if (period == 0)
+    {
+        return 0x00;
+    }
+    else if (period &amp;lt;= 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 &amp;lt;= 1)
+    {
+        return count;
+    }
+    else if (count &amp;lt;= 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 &amp;lt;= 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-&amp;gt;dst,
+        .count_log = heartbeat_publication_count_encode(p_hb_pub-&amp;gt;count),
+        .period_log = heartbeat_pubsub_period_encode(p_hb_pub-&amp;gt;period),
+        .ttl = p_hb_pub-&amp;gt;ttl,
+        .features = p_hb_pub-&amp;gt;features,
+        .netkey_index = p_hb_pub-&amp;gt;netkey_index
+    };
+    serial_cmd_rsp_send(p_cmd-&amp;gt;opcode, SERIAL_STATUS_SUCCESS, (uint8_t *)&amp;amp;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-&amp;gt;payload.cmd.mesh.hb_publication_set.dst,
+        .count        = p_cmd-&amp;gt;payload.cmd.mesh.hb_publication_set.count,
+        .period       = p_cmd-&amp;gt;payload.cmd.mesh.hb_publication_set.period,
+        .ttl          = p_cmd-&amp;gt;payload.cmd.mesh.hb_publication_set.ttl,
+        .features     = p_cmd-&amp;gt;payload.cmd.mesh.hb_publication_set.features,
+        .netkey_index = p_cmd-&amp;gt;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-&amp;gt;payload.cmd.mesh.hb_publication_set.netkey_index)
+                                           == DSM_HANDLE_INVALID)
+    {
+        status = NRF_ERROR_INVALID_DATA;
+    }
+    else
+    {
+        status = heartbeat_publication_set(&amp;amp;hb_pub);
+    }
+    serial_handler_common_cmd_rsp_nodata_on_error(p_cmd-&amp;gt;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-&amp;gt;src == NRF_MESH_ADDR_UNASSIGNED ||
+        p_hb_sub-&amp;gt;dst == NRF_MESH_ADDR_UNASSIGNED)
+    {
+        memset(&amp;amp;rsp, 0, sizeof(serial_evt_cmd_rsp_data_hb_subscription_get_t));
+    }
+    else
+    {
+        rsp.src = p_hb_sub-&amp;gt;src;
+        rsp.dst = p_hb_sub-&amp;gt;dst;
+        rsp.count_log = heartbeat_subscription_count_encode(p_hb_sub-&amp;gt;count);
+        rsp.period_log = heartbeat_pubsub_period_encode(p_hb_sub-&amp;gt;period);
+        rsp.min_hops = p_hb_sub-&amp;gt;min_hops;
+        rsp.max_hops = p_hb_sub-&amp;gt;max_hops;
+    }
+    serial_cmd_rsp_send(p_cmd-&amp;gt;opcode, SERIAL_STATUS_SUCCESS, (uint8_t *)&amp;amp;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-&amp;gt;payload.cmd.mesh.hb_subscription_set.src,
+        .dst    = p_cmd-&amp;gt;payload.cmd.mesh.hb_subscription_set.dst,
+        .period = p_cmd-&amp;gt;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(&amp;amp;hb_sub);
+    serial_handler_common_cmd_rsp_nodata_on_error(p_cmd-&amp;gt;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)
&lt;/pre&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item></channel></rss>