From bc2cecd2dbbd4f07e72bf15c9837793587275876 Mon Sep 17 00:00:00 2001 From: h2zero Date: Thu, 27 Aug 2020 10:52:06 -0600 Subject: [PATCH] Basic mesh implementation with on/off and level models. --- CMakeLists.txt | 1 - src/NimBLEDevice.cpp | 26 ++++ src/NimBLEDevice.h | 6 + src/NimBLEMeshElement.cpp | 80 ++++++++++++ src/NimBLEMeshElement.h | 41 ++++++ src/NimBLEMeshModel.cpp | 244 +++++++++++++++++++++++++++++++++++ src/NimBLEMeshModel.h | 91 +++++++++++++ src/NimBLEMeshNode.cpp | 263 ++++++++++++++++++++++++++++++++++++++ src/NimBLEMeshNode.h | 89 +++++++++++++ src/nimconfig.h | 1 + 10 files changed, 841 insertions(+), 1 deletion(-) create mode 100644 src/NimBLEMeshElement.cpp create mode 100644 src/NimBLEMeshElement.h create mode 100644 src/NimBLEMeshModel.cpp create mode 100644 src/NimBLEMeshModel.h create mode 100644 src/NimBLEMeshNode.cpp create mode 100644 src/NimBLEMeshNode.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 23b1aa5..3f9e8cd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,4 +55,3 @@ idf_component_register( PRIV_REQUIRES ${ESP_NIMBLE_PRIV_REQUIRES} ) - diff --git a/src/NimBLEDevice.cpp b/src/NimBLEDevice.cpp index bc25365..d7a4215 100644 --- a/src/NimBLEDevice.cpp +++ b/src/NimBLEDevice.cpp @@ -54,6 +54,8 @@ bool NimBLEDevice::m_synced = false; NimBLEAdvertising* NimBLEDevice::m_bleAdvertising = nullptr; #endif +NimBLEMeshNode* NimBLEDevice::m_pMeshNode = nullptr; + gap_event_handler NimBLEDevice::m_customGapHandler = nullptr; ble_gap_event_listener NimBLEDevice::m_listener; #if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL) @@ -67,6 +69,30 @@ uint16_t NimBLEDevice::m_scanDuplicateSize = CONFIG_BTDM_SCAN uint8_t NimBLEDevice::m_scanFilterMode = CONFIG_BTDM_SCAN_DUPL_TYPE; +/** + * @brief Create a new mesh node. + * @param [in] uuid The uuid to advertise before being provisioned. + * @param [in] type A bitmask of the node type to create. + * @return A point to new instance of the mesh node. + */ +NimBLEMeshNode* NimBLEDevice::createMeshNode(NimBLEUUID uuid, uint8_t type) { + if(m_pMeshNode == nullptr) { + m_pMeshNode = new NimBLEMeshNode(uuid, type); + } + + return m_pMeshNode; +} + + +/** + * @brief Get the mesh node instance. + * @return a pointer to the mesh node instance or nullptr if no node exists. + */ +NimBLEMeshNode* NimBLEDevice::getMeshNode() { + return m_pMeshNode; +} + + /** * @brief Create a new instance of a server. * @return A new instance of the server. diff --git a/src/NimBLEDevice.h b/src/NimBLEDevice.h index 08a042f..7523656 100644 --- a/src/NimBLEDevice.h +++ b/src/NimBLEDevice.h @@ -35,6 +35,8 @@ #include "NimBLEServer.h" #endif +#include "NimBLEMeshNode.h" + #include "NimBLEUtils.h" #include "NimBLESecurity.h" #include "NimBLEAddress.h" @@ -110,6 +112,9 @@ public: static NimBLEServer* getServer(); #endif + static NimBLEMeshNode* createMeshNode(NimBLEUUID uuid, uint8_t type); + static NimBLEMeshNode* getMeshNode(); + static void setPower(esp_power_level_t powerLevel, esp_ble_power_type_t powerType=ESP_BLE_PWR_TYPE_DEFAULT); static int getPower(esp_ble_power_type_t powerType=ESP_BLE_PWR_TYPE_DEFAULT); static void setCustomGapHandler(gap_event_handler handler); @@ -202,6 +207,7 @@ private: static uint16_t m_scanDuplicateSize; static uint8_t m_scanFilterMode; static std::vector m_whiteList; + static NimBLEMeshNode* m_pMeshNode; }; diff --git a/src/NimBLEMeshElement.cpp b/src/NimBLEMeshElement.cpp new file mode 100644 index 0000000..c1b0083 --- /dev/null +++ b/src/NimBLEMeshElement.cpp @@ -0,0 +1,80 @@ +/* + * NimBLEMeshElement.cpp + * + * Created: on Aug 23 2020 + * Author H2zero + * + */ + +#include "NimBLEMeshElement.h" +#include "NimBLELog.h" + +static const char* LOG_TAG = "NimBLEMeshElement"; + +NimBLEMeshElement::NimBLEMeshElement() { + m_pElem = nullptr; +} +NimBLEMeshElement::~NimBLEMeshElement() { + if(m_pElem != nullptr) { + delete m_pElem; + } + + for(auto &it : m_modelsVec) { + delete (NimBLEMeshModel*)it.user_data; + } +} + +/** + * @brief Creates a model and adds it the the elements model vector. + * @param [in] type The type of model to create. + * @param [in] pCallbacks a pointer to a callback instance for this model. + */ +void NimBLEMeshElement::createModel(uint16_t type, NimBLEMeshModelCallbacks *pCallbacks) { + for(auto &it : m_modelsVec) { + if(it.id == type) { + NIMBLE_LOGE(LOG_TAG, "Error: element already has a type %04x model", type); + return; + } + } + + NIMBLE_LOGD(LOG_TAG, "Creating model type: %04x", type); + + NimBLEMeshModel* pModel = nullptr; + + switch(type) + { + case BT_MESH_MODEL_ID_GEN_ONOFF_SRV: + pModel = new NimBLEGenOnOffSrvModel(pCallbacks); + break; + + case BT_MESH_MODEL_ID_GEN_LEVEL_SRV: + pModel = new NimBLEGenLevelSrvModel(pCallbacks); + break; + + default: + NIMBLE_LOGE(LOG_TAG, "Error: model type %04x not supported", type); + return; + } + + m_modelsVec.push_back(bt_mesh_model{{type},0,0,0, pModel->opPub,{0},{0},pModel->opList, pModel}); +} + +/** + * @brief Adds a model created outside of element context to the elements model vector. + * @param [in] model A pointer to the model instance to add. + */ +void NimBLEMeshElement::addModel(bt_mesh_model* model) { + m_modelsVec.push_back(*model); +} + +/** + * @brief Creates a bt_mesh_elem for registering with the nimble stack. + * @returns A pointer to the bt_mesh_elem created. + * @details Must not be called until all models have been added. + */ +bt_mesh_elem* NimBLEMeshElement::start() { + m_pElem = new bt_mesh_elem{0, 0, uint8_t(m_modelsVec.size()), 0, &m_modelsVec[0], NULL}; + return m_pElem; +} + + diff --git a/src/NimBLEMeshElement.h b/src/NimBLEMeshElement.h new file mode 100644 index 0000000..d88d90f --- /dev/null +++ b/src/NimBLEMeshElement.h @@ -0,0 +1,41 @@ +/* + * NimBLEMeshElement.h + * + * Created: on Aug 23 2020 + * Author H2zero + * + */ + +#ifndef MAIN_NIMBLE_MESH_ELEMENT_H_ +#define MAIN_NIMBLE_MESH_ELEMENT_H_ + +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include "nimconfig.h" + +#include "NimBLEMeshNode.h" +#include "NimBLEMeshModel.h" + +#include + +class NimBLEMeshModelCallbacks; + +class NimBLEMeshElement { +public: + void createModel(uint16_t type, NimBLEMeshModelCallbacks* pCallbacks=nullptr); + +private: + friend class NimBLEMeshNode; + + NimBLEMeshElement(); + ~NimBLEMeshElement(); + void addModel(bt_mesh_model* model); + bt_mesh_elem* start(); + + bt_mesh_elem *m_pElem; + std::vector m_modelsVec; +}; + + +#endif // CONFIG_BT_ENABLED +#endif // MAIN_NIMBLE_MESH_ELEMENT_H_ \ No newline at end of file diff --git a/src/NimBLEMeshModel.cpp b/src/NimBLEMeshModel.cpp new file mode 100644 index 0000000..cc2e1b7 --- /dev/null +++ b/src/NimBLEMeshModel.cpp @@ -0,0 +1,244 @@ +/* + * NimBLEMeshModel.cpp + * + * Created: on Aug 25 2020 + * Author H2zero + * + */ + +#include "NimBLEMeshModel.h" +#include "NimBLELog.h" + +static const char* LOG_TAG = "NimBLEMeshModel"; + +static NimBLEMeshModelCallbacks defaultCallbacks; + +/** + * @brief base model constructor + * @param [in] pCallbacks, a pointer to a callback instance for model operations + */ +NimBLEMeshModel::NimBLEMeshModel(NimBLEMeshModelCallbacks* pCallbacks) { + if(pCallbacks == nullptr) { + m_callbacks = &defaultCallbacks; + } else { + m_callbacks = pCallbacks; + } + + opList = nullptr; + opPub = nullptr; +} + + +/** + * @brief destructor + */ +NimBLEMeshModel::~NimBLEMeshModel(){ + if(opList != nullptr) { + delete[] opList; + } + + if(opPub != nullptr) { + delete[] opPub; + } +} + + +/** + * @brief Generic on/off server model constructor + * @param [in] pCallbacks, a pointer to a callback instance for model operations + */ +NimBLEGenOnOffSrvModel::NimBLEGenOnOffSrvModel(NimBLEMeshModelCallbacks* pCallbacks) +:NimBLEMeshModel(pCallbacks) +{ + // Register the opcodes for this model with the required callbacks + opList = new bt_mesh_model_op[4]{ + { BT_MESH_MODEL_OP_2(0x82, 0x01), 0, NimBLEGenOnOffSrvModel::getOnOff }, + { BT_MESH_MODEL_OP_2(0x82, 0x02), 2, NimBLEGenOnOffSrvModel::setOnOff }, + { BT_MESH_MODEL_OP_2(0x82, 0x03), 2, NimBLEGenOnOffSrvModel::setOnOffUnack }, + BT_MESH_MODEL_OP_END}; +} + + +/** + * @brief Called by the NimBLE stack to get the on/off status of the model + */ +void NimBLEGenOnOffSrvModel::getOnOff(bt_mesh_model *model, + bt_mesh_msg_ctx *ctx, + os_mbuf *buf) +{ + NimBLEGenOnOffSrvModel *pModel = (NimBLEGenOnOffSrvModel*)model->user_data; + struct os_mbuf *msg = NET_BUF_SIMPLE(3); + uint8_t *status; + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_2(0x82, 0x04)); + status = (uint8_t*)net_buf_simple_add(msg, 1); + *status = pModel->m_callbacks->getOnOff(); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + NIMBLE_LOGE(LOG_TAG, "Send status failed"); + } + + os_mbuf_free_chain(msg); +} + + +/** + * @brief Called by the NimBLE stack to set the status of the model with acknowledgement. + */ +void NimBLEGenOnOffSrvModel::setOnOff(bt_mesh_model *model, + bt_mesh_msg_ctx *ctx, + os_mbuf *buf) +{ + NimBLEGenOnOffSrvModel *pModel = (NimBLEGenOnOffSrvModel*)model->user_data; + pModel->m_callbacks->setOnOff(buf->om_data[0]); + + // send the status update + NimBLEGenOnOffSrvModel::getOnOff(model,ctx,buf); +} + + +/** + * @brief Called by the NimBLE stack to set the status of the model without acknowledgement. + */ +void NimBLEGenOnOffSrvModel::setOnOffUnack(bt_mesh_model *model, + bt_mesh_msg_ctx *ctx, + os_mbuf *buf) +{ + NimBLEGenOnOffSrvModel *pModel = (NimBLEGenOnOffSrvModel*)model->user_data; + pModel->m_callbacks->setOnOff(buf->om_data[0]); +} + + +/** + * @brief Generic level server model constructor + * @param [in] pCallbacks, a pointer to a callback instance for model operations + */ +NimBLEGenLevelSrvModel::NimBLEGenLevelSrvModel(NimBLEMeshModelCallbacks* pCallbacks) +:NimBLEMeshModel(pCallbacks) +{ + // Register the opcodes for this model with the required callbacks + opList = new bt_mesh_model_op[8]{ + { BT_MESH_MODEL_OP_2(0x82, 0x05), 0, NimBLEGenLevelSrvModel::getLevel }, + { BT_MESH_MODEL_OP_2(0x82, 0x06), 3, NimBLEGenLevelSrvModel::setLevel }, + { BT_MESH_MODEL_OP_2(0x82, 0x07), 3, NimBLEGenLevelSrvModel::setLevelUnack }, + { BT_MESH_MODEL_OP_2(0x82, 0x09), 5, NimBLEGenLevelSrvModel::setDelta }, + { BT_MESH_MODEL_OP_2(0x82, 0x0a), 5, NimBLEGenLevelSrvModel::setDeltaUnack }, + { BT_MESH_MODEL_OP_2(0x82, 0x0b), 3, NimBLEGenLevelSrvModel::setMove }, + { BT_MESH_MODEL_OP_2(0x82, 0x0c), 3, NimBLEGenLevelSrvModel::setMoveUnack }, + BT_MESH_MODEL_OP_END}; +} + + +/** + * @brief Called by the NimBLE stack to get the level value of the model. + */ +void NimBLEGenLevelSrvModel::getLevel(bt_mesh_model *model, + bt_mesh_msg_ctx *ctx, + os_mbuf *buf) +{ + NimBLEMeshModel *pModel = (NimBLEMeshModel*)model->user_data; + + struct os_mbuf *msg = NET_BUF_SIMPLE(4); + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_2(0x82, 0x08)); + net_buf_simple_add_le16(msg, pModel->m_callbacks->getLevel()); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + NIMBLE_LOGE(LOG_TAG, "Send status failed"); + } + + os_mbuf_free_chain(msg); +} + + +/** + * @brief Called by the NimBLE stack to set the level value of the model. + */ +void NimBLEGenLevelSrvModel::setLevel(bt_mesh_model *model, + bt_mesh_msg_ctx *ctx, + os_mbuf *buf) +{ + NimBLEMeshModel *pModel = (NimBLEMeshModel*)model->user_data; + pModel->m_callbacks->setLevel((int16_t) net_buf_simple_pull_le16(buf)); + + NimBLEGenLevelSrvModel::getLevel(model, ctx, buf); +} + + +/** + * @brief Called by the NimBLE stack to set the level value of the model without acknowledgement. + */ +void NimBLEGenLevelSrvModel::setLevelUnack(bt_mesh_model *model, + bt_mesh_msg_ctx *ctx, + os_mbuf *buf) +{ + NimBLEMeshModel *pModel = (NimBLEMeshModel*)model->user_data; + pModel->m_callbacks->setLevel((int16_t) net_buf_simple_pull_le16(buf)); +} + + +/** + * @brief Called by the NimBLE stack to set the level value by delta of the model. + */ +void NimBLEGenLevelSrvModel::setDelta(bt_mesh_model *model, + bt_mesh_msg_ctx *ctx, + os_mbuf *buf) +{ + NimBLEMeshModel *pModel = (NimBLEMeshModel*)model->user_data; + pModel->m_callbacks->setDelta((int16_t) net_buf_simple_pull_le16(buf)); + + NimBLEGenLevelSrvModel::getLevel(model, ctx, buf); +} + + +/** + * @brief Called by the NimBLE stack to set the level value by delta without acknowledgement. + */ +void NimBLEGenLevelSrvModel::setDeltaUnack(bt_mesh_model *model, + bt_mesh_msg_ctx *ctx, + os_mbuf *buf) +{ + NimBLEMeshModel *pModel = (NimBLEMeshModel*)model->user_data; + pModel->m_callbacks->setDelta((int16_t) net_buf_simple_pull_le16(buf)); +} + + +void NimBLEGenLevelSrvModel::setMove(bt_mesh_model *model, + bt_mesh_msg_ctx *ctx, + os_mbuf *buf) +{ +} + +void NimBLEGenLevelSrvModel::setMoveUnack(bt_mesh_model *model, + bt_mesh_msg_ctx *ctx, + os_mbuf *buf) +{ +} + + +/** + * Default model callbacks + */ +NimBLEMeshModelCallbacks::~NimBLEMeshModelCallbacks() {} + +void NimBLEMeshModelCallbacks::setOnOff(uint8_t val) { + NIMBLE_LOGD(LOG_TAG, "Gen On/Off set val: %d", val); +} + +uint8_t NimBLEMeshModelCallbacks::getOnOff() { + NIMBLE_LOGD(LOG_TAG, "Gen On/Off get"); + return 0; +} + +void NimBLEMeshModelCallbacks::setLevel(int16_t val) { + NIMBLE_LOGD(LOG_TAG, "Gen Level set val: %d", val); +} + +int16_t NimBLEMeshModelCallbacks::getLevel() { + NIMBLE_LOGD(LOG_TAG, "Gen Level get"); + return 0; +} + +void NimBLEMeshModelCallbacks::setDelta(int16_t val) { + NIMBLE_LOGD(LOG_TAG, "Gen Delta set val: %d", val); +} \ No newline at end of file diff --git a/src/NimBLEMeshModel.h b/src/NimBLEMeshModel.h new file mode 100644 index 0000000..a802171 --- /dev/null +++ b/src/NimBLEMeshModel.h @@ -0,0 +1,91 @@ +/* + * NimBLEMeshModel.h + * + * Created: on Aug 25 2020 + * Author H2zero + * + */ + +#ifndef MAIN_NIMBLE_MESH_MODEL_H_ +#define MAIN_NIMBLE_MESH_MODEL_H_ + +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include "nimconfig.h" + +#include "NimBLEMeshElement.h" + +class NimBLEMeshModelCallbacks; + +class NimBLEMeshModel { +public: + NimBLEMeshModel(NimBLEMeshModelCallbacks* pCallbacks); + ~NimBLEMeshModel(); + + bt_mesh_model_op* opList; + + bt_mesh_model_pub* opPub; + NimBLEMeshModelCallbacks* m_callbacks; +}; + +class NimBLEGenOnOffSrvModel : NimBLEMeshModel { + friend class NimBLEMeshElement; + friend class NimBLEMeshNode; + + NimBLEGenOnOffSrvModel(NimBLEMeshModelCallbacks* pCallbacks); + ~NimBLEGenOnOffSrvModel(); + + static void getOnOff(bt_mesh_model *model, + bt_mesh_msg_ctx *ctx, + os_mbuf *buf); + static void setOnOff(bt_mesh_model *model, + bt_mesh_msg_ctx *ctx, + os_mbuf *buf); + static void setOnOffUnack(bt_mesh_model *model, + bt_mesh_msg_ctx *ctx, + os_mbuf *buf); +}; + +class NimBLEGenLevelSrvModel : NimBLEMeshModel { + friend class NimBLEMeshElement; + friend class NimBLEMeshNode; + + NimBLEGenLevelSrvModel(NimBLEMeshModelCallbacks* pCallbacks); + ~NimBLEGenLevelSrvModel(); + + static void getLevel(bt_mesh_model *model, + bt_mesh_msg_ctx *ctx, + os_mbuf *buf); + static void setLevel(bt_mesh_model *model, + bt_mesh_msg_ctx *ctx, + os_mbuf *buf); + static void setLevelUnack(bt_mesh_model *model, + bt_mesh_msg_ctx *ctx, + os_mbuf *buf); + static void setDelta(bt_mesh_model *model, + bt_mesh_msg_ctx *ctx, + os_mbuf *buf); + static void setDeltaUnack(bt_mesh_model *model, + bt_mesh_msg_ctx *ctx, + os_mbuf *buf); + static void setMove(bt_mesh_model *model, + bt_mesh_msg_ctx *ctx, + os_mbuf *buf); + static void setMoveUnack(bt_mesh_model *model, + bt_mesh_msg_ctx *ctx, + os_mbuf *buf); +}; + +class NimBLEMeshModelCallbacks { +public: + virtual ~NimBLEMeshModelCallbacks(); + virtual void setOnOff(uint8_t); + virtual uint8_t getOnOff(); + virtual void setLevel(int16_t); + virtual int16_t getLevel(); + virtual void setDelta(int16_t); + +}; + +#endif // CONFIG_BT_ENABLED +#endif // MAIN_NIMBLE_MESH_MODEL_H_ \ No newline at end of file diff --git a/src/NimBLEMeshNode.cpp b/src/NimBLEMeshNode.cpp new file mode 100644 index 0000000..7c6164f --- /dev/null +++ b/src/NimBLEMeshNode.cpp @@ -0,0 +1,263 @@ +/* + * NimBLEMeshNoce.cpp + * + * Created: on July 22 2020 + * Author H2zero + * + */ + +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "NimBLEMeshNode.h" +#include "NimBLELog.h" +#include "NimBLEDevice.h" + +#include "services/gap/ble_svc_gap.h" +#include "services/gatt/ble_svc_gatt.h" + +#define CID_VENDOR 0x05C3 + +static const char* LOG_TAG = "NimBLEMeshNode"; + +/** + * Health server callback struct + */ +static const struct bt_mesh_health_srv_cb health_srv_cb = { + NimBLEHealthSrvCallbacks::faultGetCurrent, + NimBLEHealthSrvCallbacks::faultGetRegistered, + NimBLEHealthSrvCallbacks::faultClear, + NimBLEHealthSrvCallbacks::faultTest, + NimBLEHealthSrvCallbacks::attentionOn, + NimBLEHealthSrvCallbacks::attentionOff +}; + +/** + * @brief Construct a mesh node. + * @param [in] uuid The uuid used to advertise for provisioning. + * @param [in] type Bitmask of the node features supported. + */ +NimBLEMeshNode::NimBLEMeshNode(const NimBLEUUID &uuid, uint8_t type) { + assert(uuid.bitSize() == 128); + + memset(&m_serverConfig, 0, sizeof(m_serverConfig)); + memset(&m_prov, 0, sizeof(m_prov)); + memset(&m_comp, 0, sizeof(m_comp)); + memset(&m_healthPub, 0, sizeof(m_healthPub)); + + // Default server config + m_serverConfig.relay = BT_MESH_RELAY_DISABLED;/*(type & NIMBLE_MESH::RELAY) ? + BT_MESH_RELAY_ENABLED : + BT_MESH_RELAY_DISABLED;*/ + + m_serverConfig.beacon = BT_MESH_BEACON_ENABLED; + m_serverConfig.frnd = BT_MESH_FRIEND_DISABLED;/*(type & NIMBLE_MESH::FRIEND) ? + BT_MESH_FRIEND_ENABLED : + BT_MESH_FRIEND_DISABLED;*/ + + m_serverConfig.gatt_proxy = BT_MESH_GATT_PROXY_ENABLED; /*(type & NIMBLE_MESH::RELAY) ? + BT_MESH_GATT_PROXY_ENABLED : + BT_MESH_GATT_PROXY_DISABLED;*/ + + m_serverConfig.default_ttl = 7; + + // 3 transmissions with 20ms interval + m_serverConfig.net_transmit = BT_MESH_TRANSMIT(2, 20); + m_serverConfig.relay_retransmit = BT_MESH_TRANSMIT(2, 20); + + // Default health server config + m_healthSrv = {0}; + m_healthSrv.cb = &health_srv_cb; + + // Default health pub config + m_healthPub.msg = BT_MESH_HEALTH_FAULT_MSG(0); + + // Provisioning config + m_uuid = uuid; + m_prov.uuid = m_uuid.getNative()->u128.value; + m_prov.complete = NimBLEMeshNode::provComplete; + m_prov.reset = NimBLEMeshNode::provReset; + + m_configSrvModel = nullptr; + m_configHthModel = nullptr; + + // Create the primary element + m_elemVec.push_back(new NimBLEMeshElement()); +} + + +/** + * @brief Destructor, cleanup any resources created. + */ +NimBLEMeshNode::~NimBLEMeshNode() { + if(m_configSrvModel != nullptr) { + delete m_configSrvModel; + } + + if(m_configHthModel != nullptr) { + delete m_configHthModel; + } + + if(m_comp.elem != nullptr) { + free (m_comp.elem); + } +} + + +/** + * @brief Called from the callbacks when provisioning changes. + */ +void NimBLEMeshNode::setProvData(uint16_t netIdx, uint16_t addr) { + m_primAddr = addr; + m_primNetIdx = netIdx; +} + + +/** + * @brief callback, Called by NimBLE stack when provisioning is complete. + */ +void NimBLEMeshNode::provComplete(uint16_t netIdx, uint16_t addr) { + NIMBLE_LOGI(LOG_TAG, + "provisioning complete for netIdx 0x%04x addr 0x%04x", + netIdx, addr); + NimBLEDevice::getMeshNode()->setProvData(netIdx, addr); +} + + +/** + * @brief callback, Called by NimBLE stack when provisioning is reset. + */ +void NimBLEMeshNode::provReset() { + NIMBLE_LOGI(LOG_TAG, "provisioning reset"); + NimBLEDevice::getMeshNode()->setProvData(0, 0); +} + + +/** + * @brief get a pointer an element. + * @param [in] index The element vector index of the element. + * @returns a pointer to the element requested. + */ +NimBLEMeshElement* NimBLEMeshNode::getElement(uint8_t index) { + return m_elemVec[index]; +} + +/** + * @brief Create a new mesh element. + * @returns a pointer to the newly created element. + */ +NimBLEMeshElement* NimBLEMeshNode::createElement() { + m_elemVec.push_back(new NimBLEMeshElement()); + return m_elemVec.back(); +} + + +/** + * @brief Start the Mesh mode. + * @returns true on success. + */ +bool NimBLEMeshNode::start() { + // Reset and restart gatts so we can register mesh gatt + ble_gatts_reset(); + ble_svc_gap_init(); + ble_svc_gatt_init(); + bt_mesh_register_gatt(); + ble_gatts_start(); + + // Config server and primary health models are required in the primary element + // create them here and add them as the first models. + m_configSrvModel = new bt_mesh_model{{BT_MESH_MODEL_ID_CFG_SRV},0,0,0,nullptr,{0},{0},bt_mesh_cfg_srv_op,&m_serverConfig}; + for(int i = 0; i < CONFIG_BT_MESH_MODEL_KEY_COUNT; i++) { + m_configSrvModel->keys[i] = BT_MESH_KEY_UNUSED; + } + + for(int i = 0; i < CONFIG_BT_MESH_MODEL_GROUP_COUNT; i++) { + m_configSrvModel->groups[i] = BT_MESH_ADDR_UNASSIGNED; + } + + m_configHthModel = new bt_mesh_model{{BT_MESH_MODEL_ID_HEALTH_SRV},0,0,0,&m_healthPub,{0},{0},bt_mesh_health_srv_op,&m_healthSrv}; + + m_elemVec[0]->addModel(m_configSrvModel); + m_elemVec[0]->addModel(m_configHthModel); + + // setup node composition + m_comp.cid = CID_VENDOR; + m_comp.elem = (bt_mesh_elem*)calloc(m_elemVec.size(), sizeof(bt_mesh_elem)); + + if(m_comp.elem == nullptr) { + NIMBLE_LOGE(LOG_TAG, "Error: No Mem"); + return false; + } + + for(size_t i = 0; i < m_elemVec.size(); i++) { + memcpy((void*)&m_comp.elem[i], (void*)m_elemVec[i]->start(),sizeof(bt_mesh_elem)); + } + m_comp.elem_count = (uint8_t)m_elemVec.size(); + + // Use random address + ble_addr_t addr; + int err = ble_hs_id_gen_rnd(1, &addr); + assert(err == 0); + err = ble_hs_id_set_rnd(addr.val); + assert(err == 0); + + err = bt_mesh_init(addr.type, &m_prov, &m_comp); + if (err) { + NIMBLE_LOGE(LOG_TAG, "Initializing mesh failed (err %d)", err); + return false; + } + if (IS_ENABLED(CONFIG_SETTINGS)) { + settings_load(); + } + + if (bt_mesh_is_provisioned()) { + NIMBLE_LOGI(LOG_TAG, "Mesh network restored from flash"); + } + + return true; +} + + +/** + * @brief Health server callbacks + */ +int NimBLEHealthSrvCallbacks::faultGetCurrent(bt_mesh_model *model, uint8_t *test_id, + uint16_t *company_id, uint8_t *faults, + uint8_t *fault_count) +{ + NIMBLE_LOGD(LOG_TAG, "faultGetCurrent - default"); + return 0; +} + +int NimBLEHealthSrvCallbacks::faultGetRegistered(bt_mesh_model *model, uint16_t company_id, + uint8_t *test_id, uint8_t *faults, + uint8_t *fault_count) +{ + NIMBLE_LOGD(LOG_TAG, "faultGetRegistered - default"); + return 0; +} + +int NimBLEHealthSrvCallbacks::faultClear(bt_mesh_model *model, uint16_t company_id) +{ + NIMBLE_LOGD(LOG_TAG, "faultClear - default"); + return 0; +} + +int NimBLEHealthSrvCallbacks::faultTest(bt_mesh_model *model, uint8_t test_id, uint16_t company_id) +{ + NIMBLE_LOGD(LOG_TAG, "faultTest - default"); + return 0; +} + +void NimBLEHealthSrvCallbacks::attentionOn(bt_mesh_model *model) +{ + NIMBLE_LOGD(LOG_TAG, "attentionOn - default"); +} + +void NimBLEHealthSrvCallbacks::attentionOff(bt_mesh_model *model) +{ + NIMBLE_LOGD(LOG_TAG, "attentionOff - default"); +} + + +#endif // CONFIG_BT_ENABLED \ No newline at end of file diff --git a/src/NimBLEMeshNode.h b/src/NimBLEMeshNode.h new file mode 100644 index 0000000..867d7e9 --- /dev/null +++ b/src/NimBLEMeshNode.h @@ -0,0 +1,89 @@ +/* + * NimBLEMeshNode.h + * + * Created: on July 22 2020 + * Author H2zero + * + */ + +#ifndef MAIN_NIMBLE_MESH_NODE_H_ +#define MAIN_NIMBLE_MESH_NODE_H_ + +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include "nimconfig.h" + +#include "mesh/glue.h" +#include "mesh/mesh.h" + +/**** FIX COMPILATION ****/ +#undef min +#undef max +/**************************/ + +#include "NimBLEUUID.h" +#include "NimBLEMeshElement.h" + +#include + +typedef enum { + RELAY = 0x01 << 0, + BEACON = 0x01 << 1, + FRIEND = 0x01 << 2, + PROXY = 0x01 << 3, +} NIMBLE_MESH; + +class NimBLEMeshElement; + +class NimBLEMeshNode { +public: + bool start(); + NimBLEMeshElement* createElement(); + NimBLEMeshElement* getElement(uint8_t index = 0); + +private: + friend class NimBLEDevice; + friend class NimBLEMeshElement; + + NimBLEMeshNode(const NimBLEUUID &uuid, uint8_t type); + ~NimBLEMeshNode(); + static void provComplete(uint16_t netIdx, uint16_t addr); + static void provReset(); + void setProvData(uint16_t netIdx, uint16_t addr); + + bt_mesh_cfg_srv m_serverConfig; + bt_mesh_prov m_prov; + bt_mesh_comp m_comp; + uint16_t m_primAddr; + uint16_t m_primNetIdx; + bt_mesh_model* m_configSrvModel; + bt_mesh_model* m_configHthModel; + bt_mesh_health_srv m_healthSrv; + bt_mesh_model_pub m_healthPub; + NimBLEUUID m_uuid; + + std::vector m_elemVec; +}; + + +class NimBLEHealthSrvCallbacks { +public: + static int faultGetCurrent(bt_mesh_model *model, uint8_t *test_id, + uint16_t *company_id, uint8_t *faults, + uint8_t *fault_count); + + static int faultGetRegistered(bt_mesh_model *model, uint16_t company_id, + uint8_t *test_id, uint8_t *faults, + uint8_t *fault_count); + + static int faultClear(bt_mesh_model *model, uint16_t company_id); + + static int faultTest(bt_mesh_model *model, uint8_t test_id, uint16_t company_id); + + static void attentionOn(bt_mesh_model *model); + + static void attentionOff(bt_mesh_model *model); +}; + +#endif // CONFIG_BT_ENABLED +#endif // MAIN_NIMBLE_MESH_NODE_H_ \ No newline at end of file diff --git a/src/nimconfig.h b/src/nimconfig.h index 451b199..01fd124 100644 --- a/src/nimconfig.h +++ b/src/nimconfig.h @@ -84,6 +84,7 @@ /** @brief Un-comment to use external PSRAM for the NimBLE host */ #define CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_EXTERNAL 1 + /** @brief Un-comment to change the core NimBLE host runs on */ #define CONFIG_BT_NIMBLE_PINNED_TO_CORE 0