Просмотр исходного кода

ble_mesh: stack: Provisioner supports receiving heartbeat

lly 5 лет назад
Родитель
Сommit
db4b87a771

+ 22 - 0
components/bt/esp_ble_mesh/Kconfig.in

@@ -195,6 +195,28 @@ if BLE_MESH
                 This option specifies how many application keys the Provisioner can have.
                 Indeed, this value decides the number of the application keys which can be added by a Provisioner.
 
+        config BLE_MESH_PROVISIONER_RECV_HB
+            bool "Support receiving Heartbeat messages"
+            default n
+            help
+                When this option is enabled, Provisioner can call specific functions to enable
+                or disable receiving Heartbeat messages and notify them to the application layer.
+
+        if BLE_MESH_PROVISIONER_RECV_HB
+
+            config BLE_MESH_PROVISIONER_RECV_HB_FILTER_SIZE
+                int "Maximum number of filter entries for receiving Heartbeat messages"
+                default 3
+                range 1 1000
+                help
+                    This option specifies how many heartbeat filter entries Provisioner supports.
+                    The heartbeat filter (acceptlist or rejectlist) entries are used to store a
+                    list of SRC and DST which can be used to decide if a heartbeat message will
+                    be processed and notified to the application layer by Provisioner.
+                    Note: The filter is an empty rejectlist by default.
+
+        endif # BLE_MESH_PROVISIONER_RECV_HB
+
     endif # BLE_MESH_PROVISIONER
 
     # Virtual option enabled whenever Generic Provisioning layer is needed

+ 70 - 0
components/bt/esp_ble_mesh/api/core/esp_ble_mesh_networking_api.c

@@ -528,6 +528,76 @@ const uint8_t *esp_ble_mesh_provisioner_get_local_net_key(uint16_t net_idx)
 {
     return bt_mesh_provisioner_local_net_key_get(net_idx);
 }
+
+#if CONFIG_BLE_MESH_PROVISIONER_RECV_HB
+esp_err_t esp_ble_mesh_provisioner_recv_heartbeat(bool enable)
+{
+    btc_ble_mesh_prov_args_t arg = {0};
+    btc_msg_t msg = {0};
+
+    ESP_BLE_HOST_STATUS_CHECK(ESP_BLE_HOST_STATUS_ENABLED);
+
+    msg.sig = BTC_SIG_API_CALL;
+    msg.pid = BTC_PID_PROV;
+    msg.act = BTC_BLE_MESH_ACT_PROVISIONER_ENABLE_HEARTBEAT_RECV;
+
+    arg.enable_heartbeat_recv.enable = enable;
+
+    return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_mesh_prov_args_t), NULL)
+            == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL);
+}
+
+esp_err_t esp_ble_mesh_provisioner_set_heartbeat_filter_type(uint8_t type)
+{
+    btc_ble_mesh_prov_args_t arg = {0};
+    btc_msg_t msg = {0};
+
+    if (type > ESP_BLE_MESH_HEARTBEAT_FILTER_REJECTLIST) {
+        return ESP_ERR_INVALID_ARG;
+    }
+
+    ESP_BLE_HOST_STATUS_CHECK(ESP_BLE_HOST_STATUS_ENABLED);
+
+    msg.sig = BTC_SIG_API_CALL;
+    msg.pid = BTC_PID_PROV;
+    msg.act = BTC_BLE_MESH_ACT_PROVISIONER_SET_HEARTBEAT_FILTER_TYPE;
+
+    arg.set_heartbeat_filter_type.type = type;
+
+    return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_mesh_prov_args_t), NULL)
+            == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL);
+}
+
+esp_err_t esp_ble_mesh_provisioner_set_heartbeat_filter_info(uint8_t op, esp_ble_mesh_heartbeat_filter_info_t *info)
+{
+    btc_ble_mesh_prov_args_t arg = {0};
+    btc_msg_t msg = {0};
+
+    if (op > ESP_BLE_MESH_HEARTBEAT_FILTER_REMOVE || info == NULL) {
+        return ESP_ERR_INVALID_ARG;
+    }
+
+    if (!ESP_BLE_MESH_ADDR_IS_UNICAST(info->hb_src) &&
+        !ESP_BLE_MESH_ADDR_IS_UNICAST(info->hb_dst) &&
+        !ESP_BLE_MESH_ADDR_IS_GROUP(info->hb_dst)) {
+        return ESP_ERR_INVALID_ARG;
+    }
+
+    ESP_BLE_HOST_STATUS_CHECK(ESP_BLE_HOST_STATUS_ENABLED);
+
+    msg.sig = BTC_SIG_API_CALL;
+    msg.pid = BTC_PID_PROV;
+    msg.act = BTC_BLE_MESH_ACT_PROVISIONER_SET_HEARTBEAT_FILTER_INFO;
+
+    arg.set_heartbeat_filter_info.op = op;
+    arg.set_heartbeat_filter_info.hb_src = info->hb_src;
+    arg.set_heartbeat_filter_info.hb_dst = info->hb_dst;
+
+    return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_mesh_prov_args_t), NULL)
+            == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL);
+}
+#endif /* CONFIG_BLE_MESH_PROVISIONER_RECV_HB */
+
 #endif /* CONFIG_BLE_MESH_PROVISIONER */
 
 #if (CONFIG_BLE_MESH_FAST_PROV)

+ 60 - 0
components/bt/esp_ble_mesh/api/core/include/esp_ble_mesh_networking_api.h

@@ -413,6 +413,66 @@ esp_err_t esp_ble_mesh_provisioner_update_local_net_key(const uint8_t net_key[16
  */
 const uint8_t *esp_ble_mesh_provisioner_get_local_net_key(uint16_t net_idx);
 
+/**
+ * @brief         This function is called by Provisioner to enable or disable receiving
+ *                heartbeat messages.
+ *
+ * @note          If enabling receiving heartbeat message successfully, the filter will
+ *                be an empty rejectlist by default, which means all heartbeat messages
+ *                received by the Provisioner will be reported to the application layer.
+ *
+ * @param[in]     enable: Enable or disable receiving heartbeat messages.
+ *
+ * @return        ESP_OK on success or error code otherwise.
+ *
+ */
+esp_err_t esp_ble_mesh_provisioner_recv_heartbeat(bool enable);
+
+/**
+ * @brief         This function is called by Provisioner to set the heartbeat filter type.
+ *
+ * @note          1. If the filter type is not the same with the current value, then all the
+ *                   filter entries will be cleaned.
+ *                2. If the previous type is rejectlist, and changed to acceptlist, then the
+ *                   filter will be an empty acceptlist, which means no heartbeat messages
+ *                   will be reported. Users need to add SRC or DST into the filter entry,
+ *                   then heartbeat messages from the SRC or to the DST will be reported.
+ *
+ * @param[in]     type: Heartbeat filter type (acceptlist or rejectlist).
+ *
+ * @return        ESP_OK on success or error code otherwise.
+ *
+ */
+esp_err_t esp_ble_mesh_provisioner_set_heartbeat_filter_type(uint8_t type);
+
+/**
+ * @brief         This function is called by Provisioner to add or remove a heartbeat filter entry.
+ *
+ * @note          1. If the operation is "ADD", the "hb_src" can be set to the SRC (can only be a
+ *                   unicast address) of heartbeat messages, and the "hb_dst" can be set to the
+ *                   DST (unicast address or group address), at least one of them needs to be set.
+ *                   - If only one of them is set, the filter entry will only use the configured
+ *                     SRC or DST to filter heartbeat messages.
+ *                   - If both of them are set, the SRC and DST will both be used to decide if a
+ *                     heartbeat message will be handled.
+ *                   - If SRC or DST already exists in some filter entry, then the corresponding
+ *                     entry will be cleaned firstly, then a new entry will be allocated to store
+ *                     the information.
+ *                2. If the operation is "REMOVE", the "hb_src" can be set to the SRC (can only be
+ *                   a unicast address) of heartbeat messages, and the "hb_dst" can be set to the
+ *                   DST (unicast address or group address), at least one of them needs to be set.
+ *                   - The filter entry with the same SRC or DST will be removed.
+ *
+ * @param[in]     op:   Add or REMOVE
+ * @param[in]     info: Heartbeat filter entry information, including:
+ *                      hb_src - Heartbeat source address;
+ *                      hb_dst - Heartbeat destination address;
+ *
+ * @return        ESP_OK on success or error code otherwise.
+ *
+ */
+esp_err_t esp_ble_mesh_provisioner_set_heartbeat_filter_info(uint8_t op, esp_ble_mesh_heartbeat_filter_info_t *info);
+
 /**
  * @brief         This function is called to get fast provisioning application key.
  *

+ 53 - 0
components/bt/esp_ble_mesh/api/esp_ble_mesh_defs.h

@@ -801,6 +801,20 @@ typedef struct {
     uint8_t scan_rsp_data[31];  /*!< Scan response data */
 } esp_ble_mesh_ble_adv_data_t;
 
+/*!< Provisioner heartbeat filter type */
+#define ESP_BLE_MESH_HEARTBEAT_FILTER_ACCEPTLIST    0x00
+#define ESP_BLE_MESH_HEARTBEAT_FILTER_REJECTLIST    0x01
+
+/*!< Provisioner heartbeat filter operation */
+#define ESP_BLE_MESH_HEARTBEAT_FILTER_ADD           0x00
+#define ESP_BLE_MESH_HEARTBEAT_FILTER_REMOVE        0x01
+
+/** Context of Provisioner heartbeat filter information to be set */
+typedef struct {
+    uint16_t hb_src;    /*!< Heartbeat source address (unicast address) */
+    uint16_t hb_dst;    /*!< Heartbeat destination address (unicast address or group address) */
+} esp_ble_mesh_heartbeat_filter_info_t;
+
 /*!< This enum value is the event of node/provisioner/fast provisioning */
 typedef enum {
     ESP_BLE_MESH_PROV_REGISTER_COMP_EVT,                        /*!< Initialize BLE Mesh provisioning capabilities and internal data information completion event */
@@ -852,6 +866,10 @@ typedef enum {
     ESP_BLE_MESH_PROVISIONER_STORE_NODE_COMP_DATA_COMP_EVT,     /*!< Provisioner store node composition data completion event */
     ESP_BLE_MESH_PROVISIONER_DELETE_NODE_WITH_UUID_COMP_EVT,    /*!< Provisioner delete node with uuid completion event */
     ESP_BLE_MESH_PROVISIONER_DELETE_NODE_WITH_ADDR_COMP_EVT,    /*!< Provisioner delete node with unicast address completion event */
+    ESP_BLE_MESH_PROVISIONER_ENABLE_HEARTBEAT_RECV_COMP_EVT,     /*!< Provisioner start to receive heartbeat message completion event */
+    ESP_BLE_MESH_PROVISIONER_SET_HEARTBEAT_FILTER_TYPE_COMP_EVT, /*!< Provisioner set the heartbeat filter type completion event */
+    ESP_BLE_MESH_PROVISIONER_SET_HEARTBEAT_FILTER_INFO_COMP_EVT, /*!< Provisioner set the heartbeat filter information completion event */
+    ESP_BLE_MESH_PROVISIONER_RECV_HEARTBEAT_MESSAGE_EVT,        /*!< Provisioner receive heartbeat message event */
     ESP_BLE_MESH_SET_FAST_PROV_INFO_COMP_EVT,                   /*!< Set fast provisioning information (e.g. unicast address range, net_idx, etc.) completion event */
     ESP_BLE_MESH_SET_FAST_PROV_ACTION_COMP_EVT,                 /*!< Set fast provisioning action completion event */
     ESP_BLE_MESH_HEARTBEAT_MESSAGE_RECV_EVT,                    /*!< Receive Heartbeat message event */
@@ -1219,6 +1237,41 @@ typedef union {
         int err_code;                           /*!< Indicate the result of deleting node with unicast address by the Provisioner */
         uint16_t unicast_addr;                  /*!< Node unicast address */
     } provisioner_delete_node_with_addr_comp;   /*!< Event parameter of ESP_BLE_MESH_PROVISIONER_DELETE_NODE_WITH_ADDR_COMP_EVT */
+    /**
+     * @brief ESP_BLE_MESH_PROVISIONER_ENABLE_HEARTBEAT_RECV_COMP_EVT
+     */
+    struct {
+        int err_code;                           /*!< Indicate the result of enabling/disabling to receive heartbeat messages by the Provisioner */
+        bool enable;                            /*!< Indicate enabling or disabling receiving heartbeat messages */
+    } provisioner_enable_heartbeat_recv_comp;   /*!< Event parameters of ESP_BLE_MESH_PROVISIONER_ENABLE_HEARTBEAT_RECV_COMP_EVT */
+    /**
+     * @brief ESP_BLE_MESH_PROVISIONER_SET_HEARTBEAT_FILTER_TYPE_COMP_EVT
+     */
+    struct {
+        int err_code;                               /*!< Indicate the result of setting the heartbeat filter type by the Provisioner */
+        uint8_t type;                               /*!< Type of the filter used for receiving heartbeat messages */
+    } provisioner_set_heartbeat_filter_type_comp;   /*!< Event parameters of ESP_BLE_MESH_PROVISIONER_SET_HEARTBEAT_FILTER_TYPE_COMP_EVT */
+    /**
+     * @brief ESP_BLE_MESH_PROVISIONER_SET_HEARTBEAT_FILTER_INFO_COMP_EVT
+     */
+    struct {
+        int err_code;                               /*!< Indicate the result of setting the heartbeat filter address by the Provisioner */
+        uint8_t  op;                                /*!< Operation (add, remove, clean) */
+        uint16_t hb_src;                            /*!< Heartbeat source address */
+        uint16_t hb_dst;                            /*!< Heartbeat destination address */
+    } provisioner_set_heartbeat_filter_info_comp;   /*!< Event parameters of ESP_BLE_MESH_PROVISIONER_SET_HEARTBEAT_FILTER_INFO_COMP_EVT */
+    /**
+     * @brief ESP_BLE_MESH_PROVISIONER_RECV_HEARTBEAT_MESSAGE_EVT
+     */
+    struct {
+        uint16_t hb_src;            /*!< Heartbeat source address */
+        uint16_t hb_dst;            /*!< Heartbeat destination address */
+        uint8_t  init_ttl;          /*!< Heartbeat InitTTL */
+        uint8_t  rx_ttl;            /*!< Heartbeat RxTTL */
+        uint8_t  hops;              /*!< Heartbeat hops (InitTTL - RxTTL + 1) */
+        uint16_t feature;           /*!< Bit field of currently active features of the node */
+        int8_t   rssi;              /*!< RSSI of the heartbeat message */
+    } provisioner_recv_heartbeat;   /*!< Event parameters of ESP_BLE_MESH_PROVISIONER_RECV_HEARTBEAT_MESSAGE_EVT */
     /**
      * @brief ESP_BLE_MESH_SET_FAST_PROV_INFO_COMP_EVT
      */

+ 47 - 2
components/bt/esp_ble_mesh/btc/btc_ble_mesh_prov.c

@@ -820,9 +820,29 @@ const esp_ble_mesh_node_t **btc_ble_mesh_provisioner_get_node_table_entry(void)
 {
     return (const esp_ble_mesh_node_t **)bt_mesh_provisioner_get_node_table_entry();
 }
+
+#if CONFIG_BLE_MESH_PROVISIONER_RECV_HB
+static void btc_ble_mesh_provisioner_recv_heartbeat_cb(u16_t hb_src, u16_t hb_dst,
+                                                       u8_t init_ttl, u8_t rx_ttl,
+                                                       u8_t hops, u16_t feat, s8_t rssi)
+{
+    esp_ble_mesh_prov_cb_param_t mesh_param = {0};
+
+    mesh_param.provisioner_recv_heartbeat.hb_src = hb_src;
+    mesh_param.provisioner_recv_heartbeat.hb_dst = hb_dst;
+    mesh_param.provisioner_recv_heartbeat.init_ttl = init_ttl;
+    mesh_param.provisioner_recv_heartbeat.rx_ttl = rx_ttl;
+    mesh_param.provisioner_recv_heartbeat.hops = hops;
+    mesh_param.provisioner_recv_heartbeat.feature = feat;
+    mesh_param.provisioner_recv_heartbeat.rssi = rssi;
+
+    btc_ble_mesh_prov_callback(&mesh_param, ESP_BLE_MESH_PROVISIONER_RECV_HEARTBEAT_MESSAGE_EVT);
+}
+#endif /* CONFIG_BLE_MESH_PROVISIONER_RECV_HB */
+
 #endif /* CONFIG_BLE_MESH_PROVISIONER */
 
-static void btc_ble_mesh_heartbeat_msg_recv_cb(u8_t hops, u16_t feature)
+static void btc_ble_mesh_node_recv_heartbeat_cb(u8_t hops, u16_t feature)
 {
     esp_ble_mesh_prov_cb_param_t mesh_param = {0};
 
@@ -1197,7 +1217,7 @@ static void btc_ble_mesh_model_op_set(esp_ble_mesh_model_t *model)
         model->cb = (esp_ble_mesh_model_cbs_t *)&bt_mesh_cfg_srv_cb;
         struct bt_mesh_cfg_srv *srv = (struct bt_mesh_cfg_srv *)model->user_data;
         if (srv) {
-            srv->hb_sub.func = btc_ble_mesh_heartbeat_msg_recv_cb;
+            srv->hb_sub.func = btc_ble_mesh_node_recv_heartbeat_cb;
         }
         break;
     }
@@ -1997,6 +2017,31 @@ void btc_ble_mesh_prov_call_handler(btc_msg_t *msg)
         param.provisioner_delete_node_with_addr_comp.err_code =
             bt_mesh_provisioner_delete_node_with_node_addr(arg->delete_node_with_addr.unicast_addr);
         break;
+#if CONFIG_BLE_MESH_PROVISIONER_RECV_HB
+    case BTC_BLE_MESH_ACT_PROVISIONER_ENABLE_HEARTBEAT_RECV:
+        act = ESP_BLE_MESH_PROVISIONER_ENABLE_HEARTBEAT_RECV_COMP_EVT;
+        param.provisioner_enable_heartbeat_recv_comp.enable = arg->enable_heartbeat_recv.enable;
+        param.provisioner_enable_heartbeat_recv_comp.err_code =
+            bt_mesh_provisioner_recv_heartbeat(arg->enable_heartbeat_recv.enable ?
+                                               btc_ble_mesh_provisioner_recv_heartbeat_cb : NULL);
+        break;
+    case BTC_BLE_MESH_ACT_PROVISIONER_SET_HEARTBEAT_FILTER_TYPE:
+        act = ESP_BLE_MESH_PROVISIONER_SET_HEARTBEAT_FILTER_TYPE_COMP_EVT;
+        param.provisioner_set_heartbeat_filter_type_comp.type = arg->set_heartbeat_filter_type.type;
+        param.provisioner_set_heartbeat_filter_type_comp.err_code =
+            bt_mesh_provisioner_set_heartbeat_filter_type(arg->set_heartbeat_filter_type.type);
+        break;
+    case BTC_BLE_MESH_ACT_PROVISIONER_SET_HEARTBEAT_FILTER_INFO:
+        act = ESP_BLE_MESH_PROVISIONER_SET_HEARTBEAT_FILTER_INFO_COMP_EVT;
+        param.provisioner_set_heartbeat_filter_info_comp.op = arg->set_heartbeat_filter_info.op;
+        param.provisioner_set_heartbeat_filter_info_comp.hb_src = arg->set_heartbeat_filter_info.hb_src;
+        param.provisioner_set_heartbeat_filter_info_comp.hb_dst = arg->set_heartbeat_filter_info.hb_dst;
+        param.provisioner_set_heartbeat_filter_info_comp.err_code =
+            bt_mesh_provisioner_set_heartbeat_filter_info(arg->set_heartbeat_filter_info.op,
+                                                          arg->set_heartbeat_filter_info.hb_src,
+                                                          arg->set_heartbeat_filter_info.hb_dst);
+        break;
+#endif /* CONFIG_BLE_MESH_PROVISIONER_RECV_HB */
 #endif /* CONFIG_BLE_MESH_PROVISIONER */
 #if CONFIG_BLE_MESH_FAST_PROV
     case BTC_BLE_MESH_ACT_SET_FAST_PROV_INFO:

+ 14 - 0
components/bt/esp_ble_mesh/btc/include/btc_ble_mesh_prov.h

@@ -63,6 +63,9 @@ typedef enum {
     BTC_BLE_MESH_ACT_PROVISIONER_STORE_NODE_COMP_DATA,
     BTC_BLE_MESH_ACT_PROVISIONER_DELETE_NODE_WITH_UUID,
     BTC_BLE_MESH_ACT_PROVISIONER_DELETE_NODE_WITH_ADDR,
+    BTC_BLE_MESH_ACT_PROVISIONER_ENABLE_HEARTBEAT_RECV,
+    BTC_BLE_MESH_ACT_PROVISIONER_SET_HEARTBEAT_FILTER_TYPE,
+    BTC_BLE_MESH_ACT_PROVISIONER_SET_HEARTBEAT_FILTER_INFO,
     BTC_BLE_MESH_ACT_SET_FAST_PROV_INFO,
     BTC_BLE_MESH_ACT_SET_FAST_PROV_ACTION,
     BTC_BLE_MESH_ACT_LPN_ENABLE,
@@ -217,6 +220,17 @@ typedef union {
     struct ble_mesh_provisioner_delete_node_with_addr_args {
         uint16_t unicast_addr;
     } delete_node_with_addr;
+    struct {
+        bool enable;
+    } enable_heartbeat_recv;
+    struct {
+        uint8_t type;
+    } set_heartbeat_filter_type;
+    struct {
+        uint8_t  op;
+        uint16_t hb_src;
+        uint16_t hb_dst;
+    } set_heartbeat_filter_info;
     struct ble_mesh_set_fast_prov_info_args {
         uint16_t unicast_min;
         uint16_t unicast_max;

+ 231 - 0
components/bt/esp_ble_mesh/mesh_core/provisioner_main.c

@@ -1544,4 +1544,235 @@ int bt_mesh_provisioner_store_node_info(struct bt_mesh_node *node)
 }
 #endif /* CONFIG_BLE_MESH_TEST_AUTO_ENTER_NETWORK */
 
+#if CONFIG_BLE_MESH_PROVISIONER_RECV_HB
+
+#define HEARTBEAT_FILTER_ACCEPTLIST     0x00
+#define HEARTBEAT_FILTER_REJECTLIST     0x01
+
+#define HEARTBEAT_FILTER_ADD            0x00
+#define HEARTBEAT_FILTER_REMOVE         0x01
+
+#define HEARTBEAT_FILTER_WITH_SRC       BIT(0)
+#define HEARTBEAT_FILTER_WITH_DST       BIT(1)
+#define HEARTBEAT_FILTER_WITH_BOTH      (BIT(1) | BIT(0))
+
+static struct heartbeat_recv {
+    struct heartbeat_filter {
+        u8_t  type; /* Indicate if using src or dst or both to filter heartbeat messages */
+        u16_t src;  /* Heartbeat source address (unicast address) */
+        u16_t dst;  /* Heartbeat destination address (unicast address or group address) */
+    } filter[CONFIG_BLE_MESH_PROVISIONER_RECV_HB_FILTER_SIZE];
+    u8_t  type;     /* Heartbeat filter type */
+    bt_mesh_heartbeat_recv_cb_t cb; /* Heartbeat receive callback */
+} hb_rx;
+
+int bt_mesh_provisioner_recv_heartbeat(bt_mesh_heartbeat_recv_cb_t cb)
+{
+    memset(&hb_rx, 0, sizeof(hb_rx));
+
+    /* Start with an empty rejectlist, which means all heartbeat messages will be reported */
+    hb_rx.type = HEARTBEAT_FILTER_REJECTLIST;
+    hb_rx.cb = cb;
+
+    return 0;
+}
+
+int bt_mesh_provisioner_set_heartbeat_filter_type(u8_t type)
+{
+    if (type > HEARTBEAT_FILTER_REJECTLIST) {
+        BT_ERR("Invalid heartbeat filter type 0x%02x", type);
+        return -EINVAL;
+    }
+
+    /* If the heartbeat filter type is different with previous one,
+     * clear the existing filter entries.
+     */
+    if (hb_rx.type != type) {
+        memset(&hb_rx, 0, offsetof(struct heartbeat_recv, cb));
+        hb_rx.type = type;
+    }
+
+    return 0;
+}
+
+static inline u8_t get_filter_addr_type(u16_t src, u16_t dst)
+{
+    if (BLE_MESH_ADDR_IS_UNICAST(src)) {
+        if (BLE_MESH_ADDR_IS_UNICAST(dst) || BLE_MESH_ADDR_IS_GROUP(dst)) {
+            return HEARTBEAT_FILTER_WITH_BOTH;
+        } else {
+            return HEARTBEAT_FILTER_WITH_SRC;
+        }
+    } else {
+        return HEARTBEAT_FILTER_WITH_DST;
+    }
+}
+
+static int hb_filter_alloc(u16_t src, u16_t dst)
+{
+    int i;
+
+    for (i = 0; i < ARRAY_SIZE(hb_rx.filter); i++) {
+        struct heartbeat_filter *filter = &hb_rx.filter[i];
+
+        if (filter->src == BLE_MESH_ADDR_UNASSIGNED &&
+            filter->dst == BLE_MESH_ADDR_UNASSIGNED) {
+            filter->type = get_filter_addr_type(src, dst);
+            filter->src = src;
+            filter->dst = dst;
+            return 0;
+        }
+    }
+
+    BT_ERR("Heartbeat filter is full!");
+    return -ENOMEM;
+}
+
+static int hb_filter_add(u16_t src, u16_t dst)
+{
+    int i;
+
+    if (!BLE_MESH_ADDR_IS_UNICAST(src) &&
+        !BLE_MESH_ADDR_IS_UNICAST(dst) && !BLE_MESH_ADDR_IS_GROUP(dst)) {
+        BT_ERR("Invalid filter address, src 0x%04x, dst 0x%04x", src, dst);
+        return -EINVAL;
+    }
+
+    /* Check if filter entries with the same src or dst exist. */
+    for (i = 0; i < ARRAY_SIZE(hb_rx.filter); i++) {
+        struct heartbeat_filter *filter = &hb_rx.filter[i];
+
+        if ((BLE_MESH_ADDR_IS_UNICAST(src) && filter->src == src) ||
+            ((BLE_MESH_ADDR_IS_UNICAST(dst) || BLE_MESH_ADDR_IS_GROUP(dst)) &&
+            filter->dst == dst)) {
+            memset(filter, 0, sizeof(struct heartbeat_filter));
+        }
+    }
+
+    return hb_filter_alloc(src, dst);
+}
+
+static int hb_filter_remove(u16_t src, u16_t dst)
+{
+    int i;
+
+    if (!BLE_MESH_ADDR_IS_UNICAST(src) &&
+        !BLE_MESH_ADDR_IS_UNICAST(dst) && !BLE_MESH_ADDR_IS_GROUP(dst)) {
+        BT_ERR("Invalid filter address, src 0x%04x, dst 0x%04x", src, dst);
+        return -EINVAL;
+    }
+
+    for (i = 0; i < ARRAY_SIZE(hb_rx.filter); i++) {
+        struct heartbeat_filter *filter = &hb_rx.filter[i];
+
+        if ((BLE_MESH_ADDR_IS_UNICAST(src) && filter->src == src) ||
+            ((BLE_MESH_ADDR_IS_UNICAST(dst) || BLE_MESH_ADDR_IS_GROUP(dst)) &&
+            filter->dst == dst)) {
+            memset(filter, 0, sizeof(struct heartbeat_filter));
+        }
+    }
+
+    return 0;
+}
+
+int bt_mesh_provisioner_set_heartbeat_filter_info(u8_t op, u16_t src, u16_t dst)
+{
+    switch (op) {
+    case HEARTBEAT_FILTER_ADD:
+        return hb_filter_add(src, dst);
+    case HEARTBEAT_FILTER_REMOVE:
+        return hb_filter_remove(src, dst);
+    default:
+        BT_ERR("Invalid heartbeat filter opcode 0x%02x", op);
+        return -EINVAL;
+    }
+}
+
+static bool filter_with_rejectlist(struct heartbeat_filter *filter,
+                                   u16_t hb_src, u16_t hb_dst)
+{
+    switch (filter->type) {
+    case HEARTBEAT_FILTER_WITH_SRC:
+        if (hb_src == filter->src) {
+            return true;
+        }
+        break;
+    case HEARTBEAT_FILTER_WITH_DST:
+        if (hb_dst == filter->dst) {
+            return true;
+        }
+        break;
+    case HEARTBEAT_FILTER_WITH_BOTH:
+        if (hb_src == filter->src && hb_dst == filter->dst) {
+            return true;
+        }
+        break;
+    default:
+        BT_WARN("Unknown filter addr type 0x%02x", filter->type);
+        break;
+    }
+
+    return false;
+}
+
+static bool filter_with_acceptlist(struct heartbeat_filter *filter,
+                                   u16_t hb_src, u16_t hb_dst)
+{
+    switch (filter->type) {
+    case HEARTBEAT_FILTER_WITH_SRC:
+        if (hb_src == filter->src) {
+            return false;
+        }
+        break;
+    case HEARTBEAT_FILTER_WITH_DST:
+        if (hb_dst == filter->dst) {
+            return false;
+        }
+        break;
+    case HEARTBEAT_FILTER_WITH_BOTH:
+        if (hb_src == filter->src && hb_dst == filter->dst) {
+            return false;
+        }
+        break;
+    default:
+        BT_WARN("Unknown filter addr type 0x%02x", filter->type);
+        return false;
+    }
+
+    return true;
+}
+
+void bt_mesh_provisioner_heartbeat(u16_t hb_src, u16_t hb_dst,
+                                   u8_t init_ttl, u8_t rx_ttl,
+                                   u8_t hops, u16_t feat, s8_t rssi)
+{
+    int i;
+
+    if (hb_rx.cb == NULL) {
+        BT_DBG("Receiving heartbeat is not enabled");
+        return;
+    }
+
+    for (i = 0; i < ARRAY_SIZE(hb_rx.filter); i++) {
+        struct heartbeat_filter *filter = &hb_rx.filter[i];
+
+        if (hb_rx.type == HEARTBEAT_FILTER_REJECTLIST) {
+            if (filter_with_rejectlist(filter, hb_src, hb_dst)) {
+                BT_DBG("Filtered by rejectlist, src 0x%04x, dst 0x%04x", hb_src, hb_dst);
+                return;
+            }
+        } else {
+            if (filter_with_acceptlist(filter, hb_src, hb_dst)) {
+                BT_DBG("Filtered by acceptlist, src 0x%04x, dst 0x%04x", hb_src, hb_dst);
+                return;
+            }
+        }
+    }
+
+    if (hb_rx.cb) {
+        hb_rx.cb(hb_src, hb_dst, init_ttl, rx_ttl, hops, feat, rssi);
+    }
+}
+#endif /* CONFIG_BLE_MESH_PROVISIONER_RECV_HB */
+
 #endif /* CONFIG_BLE_MESH_PROVISIONER */

+ 14 - 0
components/bt/esp_ble_mesh/mesh_core/provisioner_main.h

@@ -125,6 +125,20 @@ int bt_mesh_provisioner_local_net_key_del(u16_t net_idx, bool store);
 int bt_mesh_provisioner_bind_local_model_app_idx(u16_t elem_addr, u16_t mod_id,
                                                  u16_t cid, u16_t app_idx);
 
+typedef void (* bt_mesh_heartbeat_recv_cb_t)(u16_t hb_src, u16_t hb_dst,
+                                             u8_t init_ttl, u8_t rx_ttl,
+                                             u8_t hops, u16_t feat, s8_t rssi);
+
+int bt_mesh_provisioner_recv_heartbeat(bt_mesh_heartbeat_recv_cb_t cb);
+
+int bt_mesh_provisioner_set_heartbeat_filter_type(u8_t filter_type);
+
+int bt_mesh_provisioner_set_heartbeat_filter_info(u8_t op, u16_t src, u16_t dst);
+
+void bt_mesh_provisioner_heartbeat(u16_t hb_src, u16_t hb_dst,
+                                   u8_t init_ttl, u8_t rx_ttl,
+                                   u8_t hops, u16_t feat, s8_t rssi);
+
 /* Provisioner print own element information */
 int bt_mesh_print_local_composition_data(void);
 

+ 9 - 2
components/bt/esp_ble_mesh/mesh_core/transport.c

@@ -966,7 +966,8 @@ static int trans_heartbeat(struct bt_mesh_net_rx *rx,
         return -EINVAL;
     }
 
-    if (rx->ctx.recv_dst != hb_sub_dst) {
+    if (IS_ENABLED(CONFIG_BLE_MESH_NODE) && bt_mesh_is_provisioned() &&
+        rx->ctx.recv_dst != hb_sub_dst) {
         BT_WARN("Ignoring heartbeat to non-subscribed destination");
         return 0;
     }
@@ -980,7 +981,13 @@ static int trans_heartbeat(struct bt_mesh_net_rx *rx,
            rx->ctx.addr, rx->ctx.recv_ttl, init_ttl, hops,
            (hops == 1U) ? "" : "s", feat);
 
-    bt_mesh_heartbeat(rx->ctx.addr, rx->ctx.recv_dst, hops, feat);
+    if (IS_ENABLED(CONFIG_BLE_MESH_NODE) && bt_mesh_is_provisioned()) {
+        bt_mesh_heartbeat(rx->ctx.addr, rx->ctx.recv_dst, hops, feat);
+    } else if (IS_ENABLED(CONFIG_BLE_MESH_PROVISIONER_RECV_HB) && bt_mesh_is_provisioner_en()) {
+        bt_mesh_provisioner_heartbeat(rx->ctx.addr, rx->ctx.recv_dst,
+                                      init_ttl, rx->ctx.recv_ttl,
+                                      hops, feat, rx->ctx.recv_rssi);
+    }
 
     return 0;
 }