forked from h2zero/esp-nimble-cpp
Compare commits
20 Commits
Author | SHA1 | Date | |
---|---|---|---|
4786960902 | |||
507f146477 | |||
1dedff342b | |||
d47281f40f | |||
123825d483 | |||
5b206b97a3 | |||
08a3e676f8 | |||
356459d352 | |||
06e2d188b1 | |||
afb76b8cb6 | |||
56c68d7eea | |||
bceda5a8cf | |||
799a7a84db | |||
6de87945e7 | |||
dc7d9d5b73 | |||
06037f8bf6 | |||
4d78c3013b | |||
bc2e11637e | |||
8efa7d2acc | |||
bc2cecd2db |
@ -12,6 +12,10 @@ elseif("nimble" IN_LIST BUILD_COMPONENTS OR "__nimble" IN_LIST __hack_component_
|
||||
list(APPEND ESP_NIMBLE_PRIV_REQUIRES
|
||||
nimble
|
||||
)
|
||||
elseif("bt" IN_LIST BUILD_COMPONENTS OR "__bt" IN_LIST __hack_component_targets)
|
||||
list(APPEND ESP_NIMBLE_PRIV_REQUIRES
|
||||
bt
|
||||
)
|
||||
endif()
|
||||
|
||||
if("arduino" IN_LIST BUILD_COMPONENTS OR __hack_component_targets MATCHES "__idf_arduino")
|
||||
@ -20,15 +24,7 @@ if("arduino" IN_LIST BUILD_COMPONENTS OR __hack_component_targets MATCHES "__idf
|
||||
)
|
||||
endif()
|
||||
|
||||
idf_component_register(
|
||||
REQUIRED_IDF_TARGETS
|
||||
"esp32"
|
||||
"esp32s3"
|
||||
"esp32c3"
|
||||
INCLUDE_DIRS
|
||||
"src"
|
||||
SRCS
|
||||
"src/NimBLE2904.cpp"
|
||||
set(srcs "src/NimBLE2904.cpp"
|
||||
"src/NimBLEAddress.cpp"
|
||||
"src/NimBLEAdvertisedDevice.cpp"
|
||||
"src/NimBLEAdvertising.cpp"
|
||||
@ -49,7 +45,28 @@ idf_component_register(
|
||||
"src/NimBLEServer.cpp"
|
||||
"src/NimBLEService.cpp"
|
||||
"src/NimBLEUtils.cpp"
|
||||
"src/NimBLEUUID.cpp"
|
||||
"src/NimBLEUUID.cpp")
|
||||
|
||||
if(CONFIG_BT_NIMBLE_MESH)
|
||||
list(APPEND srcs "src/NimBLEMeshCreateModel.c"
|
||||
"src/NimBLEMeshElement.cpp"
|
||||
"src/NimBLEMeshModel.cpp"
|
||||
"src/NimBLEMeshNode.cpp")
|
||||
endif()
|
||||
|
||||
if(CONFIG_NIMBLE_CPP_PERSIST_MESH_SETTINGS)
|
||||
list(APPEND srcs "src/mesh_config_store/config/config_store.c")
|
||||
endif()
|
||||
|
||||
idf_component_register(
|
||||
REQUIRED_IDF_TARGETS
|
||||
"esp32"
|
||||
"esp32s3"
|
||||
"esp32c3"
|
||||
INCLUDE_DIRS
|
||||
"src"
|
||||
SRCS "${srcs}"
|
||||
|
||||
REQUIRES
|
||||
bt
|
||||
nvs_flash
|
||||
@ -57,3 +74,7 @@ idf_component_register(
|
||||
${ESP_NIMBLE_PRIV_REQUIRES}
|
||||
)
|
||||
|
||||
if(CONFIG_BT_NIMBLE_MESH AND CONFIG_NIMBLE_CPP_PERSIST_MESH_SETTINGS)
|
||||
idf_build_set_property(COMPILE_OPTIONS "-DMYNEWT_VAL_BLE_MESH_SETTINGS=1" APPEND)
|
||||
idf_build_set_property(COMPILE_OPTIONS "-I${COMPONENT_DIR}/src/mesh_config_store" APPEND)
|
||||
endif()
|
||||
|
14
Kconfig
14
Kconfig
@ -33,7 +33,7 @@ config NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT
|
||||
Enabling this option will display return code values as text
|
||||
messages in the debug log. This will use approximately 8kB
|
||||
of flash memory.
|
||||
|
||||
|
||||
config NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT
|
||||
bool "Show NimBLE gap events as text in debug log."
|
||||
default "n"
|
||||
@ -47,7 +47,7 @@ config NIMBLE_CPP_ENABLE_ADVERTISMENT_TYPE_TEXT
|
||||
default "n"
|
||||
help
|
||||
Enabling this option will display advertisment types recieved
|
||||
while scanning as text messages in the debug log.
|
||||
while scanning as text messages in the debug log.
|
||||
This will use approximately 250 bytes of flash memory.
|
||||
|
||||
config NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
|
||||
@ -68,5 +68,13 @@ config NIMBLE_CPP_ATT_VALUE_INIT_LENGTH
|
||||
when the constructor is called. This is also the size used when a remote
|
||||
characteristic or descriptor is constructed before a value is read/notifed.
|
||||
Increasing this will reduce reallocations but increase memory footprint.
|
||||
|
||||
|
||||
config NIMBLE_CPP_PERSIST_MESH_SETTINGS
|
||||
bool "Enable persistent storage of mesh config settings."
|
||||
default "n"
|
||||
depends on BT_NIMBLE_MESH
|
||||
help
|
||||
Enabling this option will store the provisioning and app key settings
|
||||
in non-volatile storage when using NimBLE Mesh.
|
||||
|
||||
endmenu
|
||||
|
7
examples/mesh/basic_on_off_level_server/CMakeLists.txt
Normal file
7
examples/mesh/basic_on_off_level_server/CMakeLists.txt
Normal file
@ -0,0 +1,7 @@
|
||||
# The following lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
set(SUPPORTED_TARGETS esp32)
|
||||
project(basic_on_off_level_server)
|
3
examples/mesh/basic_on_off_level_server/Makefile
Normal file
3
examples/mesh/basic_on_off_level_server/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
PROJECT_NAME := basic_on_off_level_server
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
@ -0,0 +1,4 @@
|
||||
set(COMPONENT_SRCS "main.cpp")
|
||||
set(COMPONENT_ADD_INCLUDEDIRS ".")
|
||||
|
||||
register_component()
|
@ -0,0 +1,4 @@
|
||||
#
|
||||
# "main" pseudo-component makefile.
|
||||
#
|
||||
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
|
64
examples/mesh/basic_on_off_level_server/main/main.cpp
Normal file
64
examples/mesh/basic_on_off_level_server/main/main.cpp
Normal file
@ -0,0 +1,64 @@
|
||||
#include "NimBLEDevice.h"
|
||||
#include "driver/gpio.h"
|
||||
|
||||
extern "C" {void app_main(void);}
|
||||
|
||||
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
|
||||
|
||||
// LED pins
|
||||
#define LEDR GPIO_NUM_16
|
||||
#define LEDG GPIO_NUM_17
|
||||
#define LEDB GPIO_NUM_18
|
||||
#define OUTPUT_PIN (1ULL<<LEDG)
|
||||
|
||||
static uint8_t onOffVal = 0;
|
||||
static int16_t levelVal = 0;
|
||||
|
||||
class onOffSrvModelCallbacks : public NimBLEMeshModelCallbacks {
|
||||
void setOnOff(NimBLEMeshModel *pModel, uint8_t val) {
|
||||
printf("on/off set val %d, transition time: %dms\n", val, pModel->getTransTime());
|
||||
onOffVal = val;
|
||||
gpio_set_level(LEDG, !onOffVal);
|
||||
}
|
||||
|
||||
uint8_t getOnOff(NimBLEMeshModel *pModel) {
|
||||
printf("on/off get val %d\n", onOffVal);
|
||||
return onOffVal;
|
||||
}
|
||||
};
|
||||
|
||||
class levelSrvModelCallbacks : public NimBLEMeshModelCallbacks {
|
||||
void setLevel(NimBLEMeshModel *pModel, int16_t val) {
|
||||
printf("Level set val %d, transition time: %dms\n", val, pModel->getTransTime());
|
||||
levelVal = val;
|
||||
}
|
||||
|
||||
int16_t getLevel(NimBLEMeshModel *pModel) {
|
||||
printf("Level get val %d\n", levelVal);
|
||||
return levelVal;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void app_main(void) {
|
||||
gpio_config_t io_conf;
|
||||
io_conf.intr_type = (gpio_int_type_t)GPIO_PIN_INTR_DISABLE;
|
||||
io_conf.mode = GPIO_MODE_OUTPUT;
|
||||
io_conf.pin_bit_mask = OUTPUT_PIN;
|
||||
io_conf.pull_down_en = (gpio_pulldown_t)0;
|
||||
io_conf.pull_up_en = (gpio_pullup_t)0;
|
||||
gpio_config(&io_conf);
|
||||
gpio_set_level(LEDG, 1);
|
||||
|
||||
NimBLEDevice::init("");
|
||||
NimBLEMeshNode *pMesh = NimBLEDevice::createMeshNode(NimBLEUUID(SERVICE_UUID),0);
|
||||
NimBLEMeshElement* pElem = pMesh->getElement();
|
||||
NimBLEMeshModel* pModel = pElem->createModel(BT_MESH_MODEL_ID_GEN_ONOFF_SRV, new onOffSrvModelCallbacks());
|
||||
pModel->setValue(onOffVal);
|
||||
|
||||
//pElem = pMesh->createElement();
|
||||
pModel = pElem->createModel(BT_MESH_MODEL_ID_GEN_LEVEL_SRV, new levelSrvModelCallbacks());
|
||||
pModel->setValue(levelVal);
|
||||
pMesh->start();
|
||||
printf("Mesh Started");
|
||||
}
|
@ -76,6 +76,8 @@ NimBLEAdvertising* NimBLEDevice::m_bleAdvertising = nullptr;
|
||||
# endif
|
||||
#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)
|
||||
@ -90,6 +92,30 @@ uint16_t NimBLEDevice::m_scanDuplicateSize = CONFIG_BTDM_SCAN
|
||||
uint8_t NimBLEDevice::m_scanFilterMode = CONFIG_BTDM_SCAN_DUPL_TYPE;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @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.
|
||||
|
@ -38,6 +38,8 @@
|
||||
#include "NimBLEServer.h"
|
||||
#endif
|
||||
|
||||
#include "NimBLEMeshNode.h"
|
||||
|
||||
#include "NimBLEUtils.h"
|
||||
#include "NimBLESecurity.h"
|
||||
#include "NimBLEAddress.h"
|
||||
@ -115,6 +117,9 @@ public:
|
||||
static NimBLEServer* getServer();
|
||||
#endif
|
||||
|
||||
static NimBLEMeshNode* createMeshNode(NimBLEUUID uuid, uint8_t type);
|
||||
static NimBLEMeshNode* getMeshNode();
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
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);
|
||||
@ -232,6 +237,7 @@ private:
|
||||
static uint8_t m_scanFilterMode;
|
||||
#endif
|
||||
static std::vector<NimBLEAddress> m_whiteList;
|
||||
static NimBLEMeshNode* m_pMeshNode;
|
||||
};
|
||||
|
||||
|
||||
|
35
src/NimBLEMeshCreateModel.c
Normal file
35
src/NimBLEMeshCreateModel.c
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* NimBLEMeshCreateModel.cpp
|
||||
*
|
||||
* Created: on April 27 2022
|
||||
* Author H2zero
|
||||
*
|
||||
*/
|
||||
|
||||
#include "nimconfig.h"
|
||||
#if CONFIG_BT_NIMBLE_MESH
|
||||
|
||||
#include "NimBLEMeshCreateModel.h"
|
||||
|
||||
static struct bt_mesh_model_cb mod_cb = {
|
||||
//.init = modelInitCallback
|
||||
};
|
||||
|
||||
struct bt_mesh_model createConfigSrvModel(struct bt_mesh_cfg_srv* cfg) {
|
||||
struct bt_mesh_model cmod = BT_MESH_MODEL_CFG_SRV(cfg);
|
||||
return cmod;
|
||||
}
|
||||
|
||||
struct bt_mesh_model createHealthModel(struct bt_mesh_health_srv* hsrv,
|
||||
struct bt_mesh_model_pub* hpub) {
|
||||
struct bt_mesh_model hmod = BT_MESH_MODEL_HEALTH_SRV(hsrv, hpub);
|
||||
return hmod;
|
||||
}
|
||||
|
||||
struct bt_mesh_model createGenModel(int16_t _id, struct bt_mesh_model_op* op,
|
||||
struct bt_mesh_model_pub* pub, void* udata) {
|
||||
struct bt_mesh_model mod = BT_MESH_MODEL_CB(_id, op, pub, udata, &mod_cb);
|
||||
return mod;
|
||||
}
|
||||
|
||||
#endif // CONFIG_BT_NIMBLE_MESH
|
35
src/NimBLEMeshCreateModel.h
Normal file
35
src/NimBLEMeshCreateModel.h
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* NimBLEMeshCreateModel.h
|
||||
*
|
||||
* Created: on April 27 2022
|
||||
* Author H2zero
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __NIMBLE_MESH_CREATE_MODEL_H
|
||||
#define __NIMBLE_MESH_CREATE_MODEL_H
|
||||
|
||||
#include "nimconfig.h"
|
||||
#if defined(CONFIG_NIMBLE_CPP_IDF)
|
||||
# include "mesh/mesh.h"
|
||||
# include "mesh/cfg_srv.h"
|
||||
#else
|
||||
# include "nimble/nimble/host/mesh/include/mesh/mesh.h"
|
||||
# include "nimble/nimble/host/mesh/include/mesh/cfg_srv.h"
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
//int modelInitCallback(struct bt_mesh_model *model);
|
||||
struct bt_mesh_model createConfigSrvModel(struct bt_mesh_cfg_srv* cfg);
|
||||
struct bt_mesh_model createHealthModel(struct bt_mesh_health_srv* hsrv,
|
||||
struct bt_mesh_model_pub* hpub);
|
||||
struct bt_mesh_model createGenModel(int16_t _id, struct bt_mesh_model_op* op,
|
||||
struct bt_mesh_model_pub* pub, void* udata);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
145
src/NimBLEMeshElement.cpp
Normal file
145
src/NimBLEMeshElement.cpp
Normal file
@ -0,0 +1,145 @@
|
||||
/*
|
||||
* NimBLEMeshElement.cpp
|
||||
*
|
||||
* Created: on Aug 23 2020
|
||||
* Author H2zero
|
||||
*
|
||||
*/
|
||||
|
||||
#include "nimconfig.h"
|
||||
#if CONFIG_BT_NIMBLE_MESH
|
||||
|
||||
#include "NimBLEMeshElement.h"
|
||||
#include "NimBLELog.h"
|
||||
#include "NimBLEMeshCreateModel.h"
|
||||
|
||||
static const char* LOG_TAG = "NimBLEMeshElement";
|
||||
|
||||
NimBLEMeshElement::NimBLEMeshElement() {
|
||||
m_pElem = nullptr;
|
||||
m_pHealthModel = nullptr;
|
||||
}
|
||||
|
||||
|
||||
NimBLEMeshElement::~NimBLEMeshElement() {
|
||||
if(m_pElem != nullptr) {
|
||||
delete m_pElem;
|
||||
}
|
||||
|
||||
if(m_pHealthModel != nullptr) {
|
||||
delete m_pHealthModel;
|
||||
}
|
||||
|
||||
for(auto &it : m_modelsVec) {
|
||||
if(it.id != BT_MESH_MODEL_ID_HEALTH_SRV) {
|
||||
delete (NimBLEMeshModel*)it.user_data;
|
||||
}
|
||||
}
|
||||
|
||||
m_modelsVec.clear();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
NimBLEMeshModel* NimBLEMeshElement::createModel(uint16_t type, NimBLEMeshModelCallbacks *pCallbacks) {
|
||||
if(getModel(type) != nullptr) {
|
||||
NIMBLE_LOGE(LOG_TAG, "Error: element already has a type %04x model", type);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
case BT_MESH_MODEL_ID_HEALTH_SRV:
|
||||
m_pHealthModel = new NimBLEHealthSrvModel(pCallbacks);
|
||||
pModel = m_pHealthModel;
|
||||
m_modelsVec.push_back(createHealthModel(&m_pHealthModel->m_healthSrv, &pModel->m_opPub));
|
||||
return pModel;
|
||||
|
||||
default:
|
||||
NIMBLE_LOGE(LOG_TAG, "Error: model type %04x not supported", type);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
m_modelsVec.push_back(createGenModel(type, pModel->m_opList, &pModel->m_opPub, pModel));
|
||||
return 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(const bt_mesh_model & model) {
|
||||
m_modelsVec.push_back(model);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get a pointer to the model in the element with the type specified.
|
||||
* @param [in] The model type requested.
|
||||
* @returns A pointer to the model or nullptr if not found.
|
||||
*/
|
||||
NimBLEMeshModel* NimBLEMeshElement::getModel(uint16_t type) {
|
||||
if(type == BT_MESH_MODEL_ID_HEALTH_SRV) {
|
||||
return m_pHealthModel;
|
||||
}
|
||||
|
||||
for(auto &it : m_modelsVec) {
|
||||
if(it.id == type) {
|
||||
return (NimBLEMeshModel*)it.user_data;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get a pointer to a model with matching type and ID.
|
||||
* @param [in] eidx The element ID to compare.
|
||||
* @param [in] midx The model ID to compare.
|
||||
* @param [in] The model type requested.
|
||||
* @returns A pointer to the model or nullptr if not found.
|
||||
*/
|
||||
NimBLEMeshModel* NimBLEMeshElement::getModelByIdx(uint8_t eidx, uint8_t midx, uint16_t type) {
|
||||
for(auto &it : m_modelsVec) {
|
||||
if(it.elem_idx == eidx && it.mod_idx == midx) {
|
||||
if(type == BT_MESH_MODEL_ID_HEALTH_SRV) {
|
||||
return m_pHealthModel;
|
||||
} else {
|
||||
return (NimBLEMeshModel*)it.user_data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
|
||||
#endif // CONFIG_BT_NIMBLE_MESH
|
46
src/NimBLEMeshElement.h
Normal file
46
src/NimBLEMeshElement.h
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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 <vector>
|
||||
|
||||
class NimBLEMeshModelCallbacks;
|
||||
class NimBLEMeshModel;
|
||||
class NimBLEHealthSrvModel;
|
||||
|
||||
class NimBLEMeshElement {
|
||||
public:
|
||||
NimBLEMeshModel* createModel(uint16_t type, NimBLEMeshModelCallbacks* pCallbacks=nullptr);
|
||||
NimBLEMeshModel* getModel(uint16_t type);
|
||||
NimBLEMeshModel* getModelByIdx(uint8_t eidx, uint8_t midx, uint16_t type);
|
||||
|
||||
private:
|
||||
friend class NimBLEMeshNode;
|
||||
|
||||
NimBLEMeshElement();
|
||||
~NimBLEMeshElement();
|
||||
void addModel(const bt_mesh_model & model);
|
||||
bt_mesh_elem* start();
|
||||
|
||||
bt_mesh_elem *m_pElem;
|
||||
NimBLEHealthSrvModel* m_pHealthModel;
|
||||
std::vector<bt_mesh_model> m_modelsVec;
|
||||
};
|
||||
|
||||
|
||||
#endif // CONFIG_BT_ENABLED
|
||||
#endif // MAIN_NIMBLE_MESH_ELEMENT_H_
|
700
src/NimBLEMeshModel.cpp
Normal file
700
src/NimBLEMeshModel.cpp
Normal file
@ -0,0 +1,700 @@
|
||||
/*
|
||||
* NimBLEMeshModel.cpp
|
||||
*
|
||||
* Created: on Aug 25 2020
|
||||
* Author H2zero
|
||||
*
|
||||
*/
|
||||
|
||||
#include "nimconfig.h"
|
||||
#if CONFIG_BT_NIMBLE_MESH
|
||||
|
||||
#include "NimBLEMeshModel.h"
|
||||
#include "NimBLEUtils.h"
|
||||
#include "NimBLELog.h"
|
||||
|
||||
#if defined(CONFIG_NIMBLE_CPP_IDF)
|
||||
# include "nimble/nimble_port.h"
|
||||
#else
|
||||
# include "nimble/porting/nimble/include/nimble/nimble_port.h"
|
||||
#endif
|
||||
|
||||
#include "NimBLEDevice.h"
|
||||
|
||||
#define CID_VENDOR 0x05C3
|
||||
#define STANDARD_TEST_ID 0x00
|
||||
|
||||
static const char* LOG_TAG = "NimBLEMeshModel";
|
||||
|
||||
static NimBLEMeshModelCallbacks defaultCallbacks;
|
||||
|
||||
static const struct bt_mesh_health_srv_cb health_srv_cb = {
|
||||
NimBLEHealthSrvCallbacks::faultGetCurrent,
|
||||
NimBLEHealthSrvCallbacks::faultGetRegistered,
|
||||
NimBLEHealthSrvCallbacks::faultClear,
|
||||
NimBLEHealthSrvCallbacks::faultTest,
|
||||
NimBLEHealthSrvCallbacks::attentionOn,
|
||||
NimBLEHealthSrvCallbacks::attentionOff
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief base model constructor
|
||||
* @param [in] pCallbacks, a pointer to a callback instance for model operations
|
||||
*/
|
||||
NimBLEMeshModel::NimBLEMeshModel(NimBLEMeshModelCallbacks *pCallbacks,
|
||||
uint16_t initDataSize, uint16_t maxDataSize)
|
||||
: m_value(initDataSize, maxDataSize),
|
||||
m_targetValue(initDataSize, maxDataSize)
|
||||
{
|
||||
if(pCallbacks == nullptr) {
|
||||
m_callbacks = &defaultCallbacks;
|
||||
} else {
|
||||
m_callbacks = pCallbacks;
|
||||
}
|
||||
|
||||
m_opList = nullptr;
|
||||
m_lastTid = 0;
|
||||
m_lastSrcAddr = 0;
|
||||
m_lastDstAddr = 0;
|
||||
m_lastMsgTime = 0;
|
||||
m_transTime = 0;
|
||||
m_delayTime = 0;
|
||||
m_transStep = 0;
|
||||
|
||||
memset(&m_opPub, 0, sizeof(m_opPub));
|
||||
memset(&m_tdTimer, 0, sizeof(m_tdTimer));
|
||||
memset(&m_pubTimer, 0, sizeof(m_pubTimer));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief destructor
|
||||
*/
|
||||
NimBLEMeshModel::~NimBLEMeshModel() {
|
||||
if(m_opList != nullptr) {
|
||||
delete[] m_opList;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int NimBLEMeshModel::extractTransTimeDelay(os_mbuf *buf)
|
||||
{
|
||||
switch(buf->om_len) {
|
||||
case 0x00:
|
||||
m_transTime = 0;
|
||||
m_delayTime = 0;
|
||||
return 0;
|
||||
case 0x02:
|
||||
m_transTime = buf->om_data[0];
|
||||
if((m_transTime & 0x3F) == 0x3F) {
|
||||
// unknown transition time
|
||||
m_transTime = 0;
|
||||
m_delayTime = 0;
|
||||
return BLE_HS_EINVAL;
|
||||
}
|
||||
m_delayTime = buf->om_data[1];
|
||||
return 0;
|
||||
default:
|
||||
return BLE_HS_EMSGSIZE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool NimBLEMeshModel::checkRetransmit(uint8_t tid, bt_mesh_msg_ctx *ctx) {
|
||||
time_t now = time(nullptr);
|
||||
|
||||
if(m_lastTid == tid &&
|
||||
m_lastSrcAddr == ctx->addr &&
|
||||
m_lastDstAddr == ctx->recv_dst &&
|
||||
(now - m_lastMsgTime <= 6)) {
|
||||
NIMBLE_LOGD(LOG_TAG, "Ignoring retransmit");
|
||||
return true;
|
||||
}
|
||||
|
||||
m_lastTid = tid;
|
||||
m_lastSrcAddr = ctx->addr;
|
||||
m_lastDstAddr = ctx->recv_dst;
|
||||
m_lastMsgTime = now;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void NimBLEMeshModel::sendMessage(bt_mesh_model *model, bt_mesh_msg_ctx *ctx, os_mbuf *msg) {
|
||||
if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
|
||||
NIMBLE_LOGE(LOG_TAG, "Send status failed");
|
||||
}
|
||||
|
||||
os_mbuf_free_chain(msg);
|
||||
}
|
||||
|
||||
|
||||
void NimBLEMeshModel::startTdTimer(ble_npl_time_t timerMs) {
|
||||
ble_npl_time_t ticks;
|
||||
ble_npl_time_ms_to_ticks(timerMs, &ticks);
|
||||
ble_npl_callout_reset(&m_tdTimer, ticks);
|
||||
}
|
||||
|
||||
|
||||
void NimBLEMeshModel::publish() {
|
||||
ble_npl_callout_reset(&m_pubTimer, 1);
|
||||
}
|
||||
|
||||
|
||||
uint32_t NimBLEMeshModel::getTransTime() {
|
||||
return (m_transTime & 0x3F) * NimBLEUtils::meshTransTimeMs(m_transTime);
|
||||
}
|
||||
|
||||
|
||||
uint16_t NimBLEMeshModel::getDelayTime() {
|
||||
return m_delayTime * 5;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @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, 1, 1)
|
||||
{
|
||||
// Register the opcodes for this model with the required callbacks
|
||||
m_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};
|
||||
|
||||
ble_npl_callout_init(&m_tdTimer, nimble_port_get_dflt_eventq(),
|
||||
NimBLEGenOnOffSrvModel::tdTimerCb, this);
|
||||
ble_npl_callout_init(&m_pubTimer, nimble_port_get_dflt_eventq(),
|
||||
NimBLEGenOnOffSrvModel::pubTimerCb, this);
|
||||
|
||||
m_opPub.msg = NET_BUF_SIMPLE(2 + 3);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @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)
|
||||
{
|
||||
NimBLEMeshModel *pModel = (NimBLEMeshModel*)model->user_data;
|
||||
if(pModel->m_callbacks != &defaultCallbacks) {
|
||||
pModel->setValue(pModel->m_callbacks->getOnOff(pModel));
|
||||
}
|
||||
pModel->setPubMsg();
|
||||
|
||||
if (bt_mesh_model_send(model, ctx, pModel->m_opPub.msg, NULL, NULL)) {
|
||||
NIMBLE_LOGE(LOG_TAG, "Send status failed");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @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)
|
||||
{
|
||||
// Rather than duplicate code just call the unack function then send the status
|
||||
NimBLEGenOnOffSrvModel::setOnOffUnack(model,ctx,buf);
|
||||
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)
|
||||
{
|
||||
NimBLEMeshModel *pModel = (NimBLEMeshModel*)model->user_data;
|
||||
uint8_t newval = net_buf_simple_pull_u8(buf);
|
||||
uint8_t tid = net_buf_simple_pull_u8(buf);
|
||||
|
||||
if(pModel->checkRetransmit(tid, ctx)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(pModel->extractTransTimeDelay(buf) != 0) {
|
||||
NIMBLE_LOGI(LOG_TAG, "Transition time / delay data error");
|
||||
return;
|
||||
}
|
||||
|
||||
// stop the transition timer to handle the new input
|
||||
ble_npl_callout_stop(&pModel->m_tdTimer);
|
||||
|
||||
// Mesh spec says transition to "ON state" happens immediately
|
||||
// after delay, so ignore the transition time.
|
||||
if(newval == 1) {
|
||||
pModel->m_transTime = 0;
|
||||
}
|
||||
|
||||
ble_npl_time_t timerMs = 0;
|
||||
|
||||
if(newval != pModel->m_value[0]) {
|
||||
pModel->m_targetValue.setValue(&newval, sizeof(newval));
|
||||
|
||||
if(pModel->m_delayTime > 0) {
|
||||
timerMs = 5 * pModel->m_delayTime;
|
||||
} else if(pModel->m_transTime & 0x3F) {
|
||||
timerMs = NimBLEUtils::meshTransTimeMs(pModel->m_transTime);
|
||||
pModel->m_transTime -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
if(timerMs > 0) {
|
||||
pModel->startTdTimer(timerMs);
|
||||
} else {
|
||||
pModel->m_value = pModel->m_targetValue;
|
||||
pModel->m_callbacks->setOnOff(pModel, pModel->m_value[0]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void NimBLEGenOnOffSrvModel::tdTimerCb(ble_npl_event *event) {
|
||||
NimBLEMeshModel *pModel = (NimBLEMeshModel*)event->arg;
|
||||
if(pModel->m_delayTime > 0) {
|
||||
pModel->m_delayTime = 0;
|
||||
}
|
||||
|
||||
if((pModel->m_transTime & 0x3F) && pModel->m_targetValue[0] == 0) {
|
||||
pModel->startTdTimer(NimBLEUtils::meshTransTimeMs(pModel->m_transTime));
|
||||
pModel->m_transTime -= 1;
|
||||
pModel->publish();
|
||||
return;
|
||||
}
|
||||
|
||||
pModel->m_transTime = 0;
|
||||
pModel->m_value = pModel->m_targetValue;
|
||||
pModel->m_callbacks->setOnOff(pModel, pModel->m_value[0]);
|
||||
}
|
||||
|
||||
|
||||
void NimBLEGenOnOffSrvModel::pubTimerCb(ble_npl_event *event) {
|
||||
NimBLEMeshModel *pModel = (NimBLEMeshModel*)event->arg;
|
||||
pModel->setPubMsg();
|
||||
|
||||
int err = bt_mesh_model_publish(pModel->m_opPub.mod);
|
||||
if(err != 0) {
|
||||
NIMBLE_LOGD(LOG_TAG, "Publish rc: %d",err);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void NimBLEGenOnOffSrvModel::setPubMsg() {
|
||||
bt_mesh_model_msg_init(m_opPub.msg, BT_MESH_MODEL_OP_2(0x82, 0x04));
|
||||
net_buf_simple_add_u8(m_opPub.msg, m_value[0]);
|
||||
if(m_transTime > 0) {
|
||||
net_buf_simple_add_u8(m_opPub.msg, m_targetValue[0]);
|
||||
// If we started the transition timer in setOnOff we need to correct the reported remaining time.
|
||||
net_buf_simple_add_u8(m_opPub.msg, (m_delayTime > 0) ?
|
||||
m_transTime : m_transTime + 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void NimBLEGenOnOffSrvModel::setValue(uint8_t *val, size_t len) {
|
||||
if(len != sizeof(uint8_t)) {
|
||||
NIMBLE_LOGE(LOG_TAG, "NimBLEGenOnOffSrvModel: Incorrect value length");
|
||||
return;
|
||||
}
|
||||
m_value.setValue(val, len);
|
||||
}
|
||||
|
||||
|
||||
void NimBLEGenOnOffSrvModel::setTargetValue(uint8_t *val, size_t len) {
|
||||
if(len != sizeof(uint8_t)) {
|
||||
NIMBLE_LOGE(LOG_TAG, "NimBLEGenOnOffSrvModel: Incorrect target value length");
|
||||
return;
|
||||
}
|
||||
m_targetValue.setValue(val, len);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Generic level server model constructor
|
||||
* @param [in] pCallbacks, a pointer to a callback instance for model operations
|
||||
*/
|
||||
NimBLEGenLevelSrvModel::NimBLEGenLevelSrvModel(NimBLEMeshModelCallbacks *pCallbacks)
|
||||
:NimBLEMeshModel(pCallbacks, 2, 2)
|
||||
{
|
||||
// Register the opcodes for this model with the required callbacks
|
||||
m_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};
|
||||
|
||||
ble_npl_callout_init(&m_tdTimer, nimble_port_get_dflt_eventq(),
|
||||
NimBLEGenLevelSrvModel::tdTimerCb, this);
|
||||
ble_npl_callout_init(&m_pubTimer, nimble_port_get_dflt_eventq(),
|
||||
NimBLEGenLevelSrvModel::pubTimerCb, this);
|
||||
m_opPub.msg = NET_BUF_SIMPLE(2 + 5);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @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;
|
||||
if(pModel->m_callbacks != &defaultCallbacks) {
|
||||
pModel->setValue(pModel->m_callbacks->getLevel(pModel));
|
||||
}
|
||||
pModel->setPubMsg();
|
||||
|
||||
if (bt_mesh_model_send(model, ctx, pModel->m_opPub.msg, NULL, NULL)) {
|
||||
NIMBLE_LOGE(LOG_TAG, "Send status failed");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @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)
|
||||
{
|
||||
NimBLEGenLevelSrvModel::setLevelUnack(model, ctx, 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;
|
||||
int16_t newval = (int16_t) net_buf_simple_pull_le16(buf);
|
||||
uint8_t tid = net_buf_simple_pull_u8(buf);
|
||||
|
||||
if(pModel->checkRetransmit(tid, ctx)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(pModel->extractTransTimeDelay(buf) != 0) {
|
||||
NIMBLE_LOGI(LOG_TAG, "Transition time / delay data error");
|
||||
return;
|
||||
}
|
||||
|
||||
// stop the transition timer to handle the new input
|
||||
ble_npl_callout_stop(&pModel->m_tdTimer);
|
||||
|
||||
ble_npl_time_t timerMs = 0;
|
||||
|
||||
int16_t curval = pModel->m_value.getValue<int16_t>(nullptr, true);
|
||||
|
||||
if(newval != curval) {
|
||||
pModel->m_targetValue.setValue(newval);
|
||||
|
||||
if(pModel->m_delayTime > 0) {
|
||||
timerMs = 5 * pModel->m_delayTime;
|
||||
}
|
||||
|
||||
if(pModel->m_transTime & 0x3F) {
|
||||
pModel->m_transStep = -1 * ((curval - newval) / (pModel->m_transTime & 0x3F));
|
||||
if(timerMs == 0) {
|
||||
timerMs = NimBLEUtils::meshTransTimeMs(pModel->m_transTime);
|
||||
pModel->m_transTime -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(timerMs > 0) {
|
||||
pModel->startTdTimer(timerMs);
|
||||
} else {
|
||||
pModel->m_value = pModel->m_targetValue;
|
||||
pModel->m_callbacks->setLevel(pModel, newval);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @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)
|
||||
{
|
||||
NimBLEGenLevelSrvModel::setDeltaUnack(model, ctx, 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;
|
||||
int32_t delta = (int32_t) net_buf_simple_pull_le32(buf);
|
||||
|
||||
int32_t temp32 = pModel->m_value.getValue<int16_t>(nullptr, true) + delta;
|
||||
if (temp32 < INT16_MIN) {
|
||||
temp32 = INT16_MIN;
|
||||
} else if (temp32 > INT16_MAX) {
|
||||
temp32 = INT16_MAX;
|
||||
}
|
||||
|
||||
net_buf_simple_push_le16(buf, (uint16_t)temp32);
|
||||
NimBLEGenLevelSrvModel::setLevelUnack(model, ctx, buf);
|
||||
}
|
||||
|
||||
|
||||
void NimBLEGenLevelSrvModel::setMove(bt_mesh_model *model,
|
||||
bt_mesh_msg_ctx *ctx,
|
||||
os_mbuf *buf)
|
||||
{
|
||||
NimBLEGenLevelSrvModel::setMoveUnack(model, ctx, buf);
|
||||
NimBLEGenLevelSrvModel::getLevel(model, ctx, buf);
|
||||
}
|
||||
|
||||
|
||||
void NimBLEGenLevelSrvModel::setMoveUnack(bt_mesh_model *model,
|
||||
bt_mesh_msg_ctx *ctx,
|
||||
os_mbuf *buf)
|
||||
{
|
||||
int16_t delta = (int16_t) net_buf_simple_pull_le16(buf);
|
||||
// Check if a transition time is present, if not then ignore this message.
|
||||
// See: bluetooth mesh specifcation
|
||||
if(buf->om_len < 3) {
|
||||
return;
|
||||
}
|
||||
put_le32(net_buf_simple_push(buf, 4), (int32_t)delta);
|
||||
NimBLEGenLevelSrvModel::setDeltaUnack(model, ctx, buf);
|
||||
}
|
||||
|
||||
|
||||
void NimBLEGenLevelSrvModel::tdTimerCb(ble_npl_event *event) {
|
||||
NimBLEMeshModel *pModel = (NimBLEMeshModel*)event->arg;
|
||||
if(pModel->m_delayTime > 0) {
|
||||
pModel->m_delayTime = 0;
|
||||
}
|
||||
|
||||
if(pModel->m_transTime & 0x3F) {
|
||||
int16_t newval = pModel->m_value.getValue<int16_t>(nullptr, true) + pModel->m_transStep;
|
||||
pModel->m_value.setValue(newval);
|
||||
pModel->m_callbacks->setLevel(pModel, newval);
|
||||
pModel->startTdTimer(NimBLEUtils::meshTransTimeMs(pModel->m_transTime));
|
||||
pModel->m_transTime -= 1;
|
||||
return;
|
||||
}
|
||||
|
||||
pModel->m_transTime = 0;
|
||||
pModel->m_value = pModel->m_targetValue;
|
||||
pModel->m_callbacks->setLevel(pModel, pModel->m_value.getValue<int16_t>(nullptr, true));
|
||||
}
|
||||
|
||||
|
||||
void NimBLEGenLevelSrvModel::pubTimerCb(ble_npl_event *event) {
|
||||
NimBLEMeshModel *pModel = (NimBLEMeshModel*)event->arg;
|
||||
pModel->setPubMsg();
|
||||
|
||||
int err = bt_mesh_model_publish(pModel->m_opPub.mod);
|
||||
if(err != 0) {
|
||||
NIMBLE_LOGD(LOG_TAG, "Publish rc: %d",err);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void NimBLEGenLevelSrvModel::setPubMsg() {
|
||||
bt_mesh_model_msg_init(m_opPub.msg, BT_MESH_MODEL_OP_2(0x82, 0x08));
|
||||
net_buf_simple_add_le16(m_opPub.msg, m_value.getValue<int16_t>(nullptr, true));
|
||||
if(m_transTime > 0) {
|
||||
net_buf_simple_add_le16(m_opPub.msg, m_targetValue.getValue<int16_t>(nullptr, true));
|
||||
// If we started the transition timer in setOnOff we need to correct the reported remaining time.
|
||||
net_buf_simple_add_u8(m_opPub.msg, (m_delayTime > 0) ?
|
||||
m_transTime : m_transTime + 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void NimBLEGenLevelSrvModel::setValue(uint8_t *val, size_t len) {
|
||||
if(len != sizeof(int16_t)) {
|
||||
NIMBLE_LOGE(LOG_TAG, "NimBLEGenLevelSrvModel: Incorrect value length");
|
||||
return;
|
||||
}
|
||||
m_value.setValue(val, len);
|
||||
}
|
||||
|
||||
|
||||
void NimBLEGenLevelSrvModel::setTargetValue(uint8_t *val, size_t len) {
|
||||
if(len != sizeof(int16_t)) {
|
||||
NIMBLE_LOGE(LOG_TAG, "NimBLEGenLevelSrvModel: Incorrect target value length");
|
||||
return;
|
||||
}
|
||||
m_targetValue.setValue(val, len);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Health server model constructor
|
||||
* @param [in] pCallbacks, a pointer to a callback instance for model operations
|
||||
*/
|
||||
NimBLEHealthSrvModel::NimBLEHealthSrvModel(NimBLEMeshModelCallbacks *pCallbacks)
|
||||
:NimBLEMeshModel(pCallbacks, 1, 1)
|
||||
{
|
||||
memset(&m_healthSrv, 0, sizeof(m_healthSrv));
|
||||
m_healthSrv.cb = &health_srv_cb;
|
||||
m_opPub.msg = NET_BUF_SIMPLE(1 + 3);
|
||||
m_hasFault = false;
|
||||
m_testId = 0;
|
||||
}
|
||||
|
||||
|
||||
void NimBLEHealthSrvModel::setFault(uint8_t fault) {
|
||||
m_faults.push_back(fault);
|
||||
m_hasFault = true;
|
||||
}
|
||||
|
||||
|
||||
void NimBLEHealthSrvModel::clearFaults() {
|
||||
m_faults.clear();
|
||||
m_hasFault = false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Default model callbacks
|
||||
*/
|
||||
NimBLEMeshModelCallbacks::~NimBLEMeshModelCallbacks() {}
|
||||
|
||||
void NimBLEMeshModelCallbacks::setOnOff(NimBLEMeshModel *pModel, uint8_t val) {
|
||||
NIMBLE_LOGD(LOG_TAG, "Gen On/Off set val: %d", val);
|
||||
}
|
||||
|
||||
uint8_t NimBLEMeshModelCallbacks::getOnOff(NimBLEMeshModel *pModel) {
|
||||
NIMBLE_LOGD(LOG_TAG, "Gen On/Off get");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void NimBLEMeshModelCallbacks::setLevel(NimBLEMeshModel *pModel, int16_t val) {
|
||||
NIMBLE_LOGD(LOG_TAG, "Gen Level set val: %d", val);
|
||||
}
|
||||
|
||||
int16_t NimBLEMeshModelCallbacks::getLevel(NimBLEMeshModel *pModel) {
|
||||
NIMBLE_LOGD(LOG_TAG, "Gen Level get");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void NimBLEMeshModelCallbacks::attentionOn(NimBLEMeshModel *pModel) {
|
||||
NIMBLE_LOGD(LOG_TAG, "Attention On Default");
|
||||
}
|
||||
|
||||
void NimBLEMeshModelCallbacks::attentionOff(NimBLEMeshModel *pModel) {
|
||||
NIMBLE_LOGD(LOG_TAG, "Attention Off Default");
|
||||
}
|
||||
|
||||
void NimBLEMeshModelCallbacks::faultTest(NimBLEMeshModel *pModel) {
|
||||
NIMBLE_LOGD(LOG_TAG, "Fault Test");
|
||||
}
|
||||
|
||||
void NimBLEMeshModelCallbacks::faultClear(NimBLEMeshModel *pModel) {
|
||||
NIMBLE_LOGD(LOG_TAG, "Fault Clear");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @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");
|
||||
|
||||
NimBLEHealthSrvModel* pModel = (NimBLEHealthSrvModel*)NimBLEDevice::getMeshNode()->getHealthModel(model);
|
||||
*test_id = pModel->m_testId;
|
||||
*company_id = CID_VENDOR;
|
||||
*fault_count = std::min(*(size_t*)fault_count, pModel->m_faults.size());
|
||||
memcpy(faults, &pModel->m_faults[0], *fault_count);
|
||||
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");
|
||||
|
||||
if (company_id != CID_VENDOR) {
|
||||
return -BLE_HS_EINVAL;
|
||||
}
|
||||
|
||||
NimBLEHealthSrvModel* pModel = (NimBLEHealthSrvModel*)NimBLEDevice::getMeshNode()->getHealthModel(model);
|
||||
*test_id = pModel->m_testId;
|
||||
*fault_count = std::min(*(size_t*)fault_count, pModel->m_faults.size());
|
||||
memcpy(faults, &pModel->m_faults[0], *fault_count);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int NimBLEHealthSrvCallbacks::faultClear(bt_mesh_model *model, uint16_t company_id)
|
||||
{
|
||||
NIMBLE_LOGD(LOG_TAG, "faultClear - default");
|
||||
|
||||
if (company_id != CID_VENDOR) {
|
||||
return -BLE_HS_EINVAL;
|
||||
}
|
||||
|
||||
NimBLEHealthSrvModel* pModel = (NimBLEHealthSrvModel*)NimBLEDevice::getMeshNode()->getHealthModel(model);
|
||||
pModel->m_callbacks->faultClear(pModel);
|
||||
pModel->clearFaults();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int NimBLEHealthSrvCallbacks::faultTest(bt_mesh_model *model, uint8_t test_id, uint16_t company_id)
|
||||
{
|
||||
NIMBLE_LOGD(LOG_TAG, "faultTest - default");
|
||||
|
||||
if (company_id != CID_VENDOR) {
|
||||
return -BLE_HS_EINVAL;
|
||||
}
|
||||
|
||||
if (test_id != STANDARD_TEST_ID) {
|
||||
return -BLE_HS_EINVAL;
|
||||
}
|
||||
|
||||
NimBLEHealthSrvModel* pModel = (NimBLEHealthSrvModel*)NimBLEDevice::getMeshNode()->getHealthModel(model);
|
||||
pModel->setFault(0);
|
||||
pModel->m_testId = test_id;
|
||||
pModel->m_callbacks->faultTest(pModel);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void NimBLEHealthSrvCallbacks::attentionOn(bt_mesh_model *model)
|
||||
{
|
||||
NIMBLE_LOGD(LOG_TAG, "attentionOn - default");
|
||||
NimBLEMeshModel* pModel = NimBLEDevice::getMeshNode()->getHealthModel(model);
|
||||
pModel->m_callbacks->attentionOn(pModel);
|
||||
}
|
||||
|
||||
void NimBLEHealthSrvCallbacks::attentionOff(bt_mesh_model *model)
|
||||
{
|
||||
NIMBLE_LOGD(LOG_TAG, "attentionOff - default");
|
||||
NimBLEMeshModel* pModel = NimBLEDevice::getMeshNode()->getHealthModel(model);
|
||||
pModel->m_callbacks->attentionOff(pModel);
|
||||
}
|
||||
|
||||
#endif // CONFIG_BT_NIMBLE_MESH
|
197
src/NimBLEMeshModel.h
Normal file
197
src/NimBLEMeshModel.h
Normal file
@ -0,0 +1,197 @@
|
||||
/*
|
||||
* 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"
|
||||
#include "NimBLEAttValue.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
class NimBLEMeshModelCallbacks;
|
||||
|
||||
class NimBLEMeshModel {
|
||||
public:
|
||||
NimBLEMeshModel(NimBLEMeshModelCallbacks* pCallbacks,
|
||||
uint16_t initDataSize = CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH,
|
||||
uint16_t maxDataSize = BLE_ATT_ATTR_MAX_LEN);
|
||||
virtual ~NimBLEMeshModel();
|
||||
int extractTransTimeDelay(os_mbuf *buf);
|
||||
bool checkRetransmit(uint8_t tid, bt_mesh_msg_ctx *ctx);
|
||||
void sendMessage(bt_mesh_model *model, bt_mesh_msg_ctx *ctx, os_mbuf *msg);
|
||||
void startTdTimer(ble_npl_time_t timerMs);
|
||||
void publish();
|
||||
uint32_t getTransTime();
|
||||
uint16_t getDelayTime();
|
||||
virtual void setPubMsg(){};
|
||||
virtual void setValue(uint8_t *val, size_t len){};
|
||||
virtual void setTargetValue(uint8_t *val, size_t len){};
|
||||
virtual void setFault(uint8_t){};
|
||||
virtual void clearFaults(){};
|
||||
|
||||
template<typename T>
|
||||
void setValue(const T &s) {
|
||||
setValue((uint8_t*)&s, sizeof(T));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void setTargetValue(const T &s) {
|
||||
setTargetValue((uint8_t*)&s, sizeof(T));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void getValue(T &s) {
|
||||
s = (T)m_value[0];
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void getTargetValue(T &s) {
|
||||
s = (T)m_targetValue[0];
|
||||
}
|
||||
|
||||
bt_mesh_model_op* m_opList;
|
||||
bt_mesh_model_pub m_opPub;
|
||||
NimBLEMeshModelCallbacks* m_callbacks;
|
||||
uint8_t m_lastTid;
|
||||
uint16_t m_lastSrcAddr;
|
||||
uint16_t m_lastDstAddr;
|
||||
time_t m_lastMsgTime;
|
||||
uint8_t m_transTime;
|
||||
uint8_t m_delayTime;
|
||||
NimBLEAttValue m_value;
|
||||
NimBLEAttValue m_targetValue;
|
||||
int16_t m_transStep;
|
||||
|
||||
ble_npl_callout m_tdTimer;
|
||||
ble_npl_callout m_pubTimer;
|
||||
};
|
||||
|
||||
|
||||
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);
|
||||
static void tdTimerCb(ble_npl_event *event);
|
||||
static void pubTimerCb(ble_npl_event *event);
|
||||
|
||||
void setPubMsg() override;
|
||||
void setValue(uint8_t *val, size_t len) override;
|
||||
void setTargetValue(uint8_t *val, size_t len) override;
|
||||
};
|
||||
|
||||
|
||||
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);
|
||||
static void tdTimerCb(ble_npl_event *event);
|
||||
static void pubTimerCb(ble_npl_event *event);
|
||||
|
||||
void setPubMsg() override;
|
||||
void setValue(uint8_t *val, size_t len) override;
|
||||
void setTargetValue(uint8_t *val, size_t len) override;
|
||||
};
|
||||
|
||||
|
||||
class NimBLEHealthSrvModel : NimBLEMeshModel {
|
||||
friend class NimBLEMeshElement;
|
||||
friend class NimBLEMeshNode;
|
||||
friend class NimBLEHealthSrvCallbacks;
|
||||
|
||||
NimBLEHealthSrvModel(NimBLEMeshModelCallbacks *pCallbacks);
|
||||
~NimBLEHealthSrvModel(){};
|
||||
|
||||
public:
|
||||
void setFault(uint8_t) override;
|
||||
void clearFaults() override;
|
||||
|
||||
private:
|
||||
bt_mesh_health_srv m_healthSrv;
|
||||
bool m_hasFault;
|
||||
uint8_t m_testId;
|
||||
std::vector<uint8_t> m_faults;
|
||||
};
|
||||
|
||||
|
||||
class NimBLEMeshModelCallbacks {
|
||||
public:
|
||||
virtual ~NimBLEMeshModelCallbacks();
|
||||
virtual void setOnOff(NimBLEMeshModel *pModel, uint8_t val);
|
||||
virtual uint8_t getOnOff(NimBLEMeshModel *pModel);
|
||||
virtual void setLevel(NimBLEMeshModel *pModel, int16_t val);
|
||||
virtual int16_t getLevel(NimBLEMeshModel *pModel);
|
||||
virtual void attentionOn(NimBLEMeshModel *pModel);
|
||||
virtual void attentionOff(NimBLEMeshModel *pModel);
|
||||
virtual void faultTest(NimBLEMeshModel *pModel);
|
||||
virtual void faultClear(NimBLEMeshModel *pModel);
|
||||
|
||||
};
|
||||
|
||||
|
||||
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_MODEL_H_
|
209
src/NimBLEMeshNode.cpp
Normal file
209
src/NimBLEMeshNode.cpp
Normal file
@ -0,0 +1,209 @@
|
||||
/*
|
||||
* NimBLEMeshNode.cpp
|
||||
*
|
||||
* Created: on July 22 2020
|
||||
* Author H2zero
|
||||
*
|
||||
*/
|
||||
|
||||
#include "nimconfig.h"
|
||||
#if CONFIG_BT_NIMBLE_MESH
|
||||
|
||||
#include "NimBLEMeshNode.h"
|
||||
#include "NimBLELog.h"
|
||||
#include "NimBLEDevice.h"
|
||||
#include "NimBLEMeshCreateModel.h"
|
||||
|
||||
#if defined(CONFIG_NIMBLE_CPP_IDF)
|
||||
# include "services/gap/ble_svc_gap.h"
|
||||
# include "services/gatt/ble_svc_gatt.h"
|
||||
#else
|
||||
# include "nimble/nimble/host/services/gap/include/services/gap/ble_svc_gap.h"
|
||||
# include "nimble/nimble/host/services/gatt/include/services/gatt/ble_svc_gatt.h"
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#define CID_VENDOR 0x05C3
|
||||
|
||||
static const char* LOG_TAG = "NimBLEMeshNode";
|
||||
|
||||
|
||||
/**
|
||||
* @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));
|
||||
|
||||
// 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);
|
||||
|
||||
// Provisioning config
|
||||
m_uuid = uuid;
|
||||
m_prov.uuid = m_uuid.getNative()->u128.value;
|
||||
m_prov.complete = NimBLEMeshNode::provComplete;
|
||||
m_prov.reset = NimBLEMeshNode::provReset;
|
||||
|
||||
// Create the primary element
|
||||
m_elemVec.push_back(new NimBLEMeshElement());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Destructor, cleanup any resources created.
|
||||
*/
|
||||
NimBLEMeshNode::~NimBLEMeshNode() {
|
||||
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 Get a pointer to the health model instance that matches the ID's of the input model.
|
||||
* @param [in] model A pointer to the NimBLE internal model instance.
|
||||
* @returns A pointer to the model.
|
||||
*/
|
||||
NimBLEMeshModel* NimBLEMeshNode::getHealthModel(bt_mesh_model *model) {
|
||||
NimBLEMeshModel* pModel;
|
||||
|
||||
for(auto &it : m_elemVec) {
|
||||
pModel = it->getModelByIdx(model->elem_idx, model->mod_idx, BT_MESH_MODEL_ID_HEALTH_SRV);
|
||||
if(pModel != nullptr) {
|
||||
return pModel;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @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_elemVec[0]->addModel(createConfigSrvModel(&m_serverConfig));
|
||||
|
||||
if(m_elemVec[0]->getModel(BT_MESH_MODEL_ID_HEALTH_SRV) == nullptr) {
|
||||
m_elemVec[0]->createModel(BT_MESH_MODEL_ID_HEALTH_SRV);
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
#endif // CONFIG_BT_NIMBLE_MESH
|
76
src/NimBLEMeshNode.h
Normal file
76
src/NimBLEMeshNode.h
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* NimBLEMeshNode.h
|
||||
*
|
||||
* Created: on July 22 2020
|
||||
* Author H2zero
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MAIN_NIMBLE_MESH_NODE_H_
|
||||
#define MAIN_NIMBLE_MESH_NODE_H_
|
||||
|
||||
#include "nimconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wpointer-arith"
|
||||
#if defined(CONFIG_NIMBLE_CPP_IDF)
|
||||
# include "mesh/glue.h"
|
||||
# include "mesh/mesh.h"
|
||||
#else
|
||||
# include "nimble/nimble/host/mesh/include/mesh/glue.h"
|
||||
# include "nimble/nimble/host/mesh/include/mesh/mesh.h"
|
||||
#endif
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
/**** FIX COMPILATION ****/
|
||||
#undef min
|
||||
#undef max
|
||||
/**************************/
|
||||
|
||||
#include "NimBLEUUID.h"
|
||||
#include "NimBLEMeshElement.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
class NimBLEMeshModel;
|
||||
|
||||
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);
|
||||
NimBLEMeshModel* getHealthModel(bt_mesh_model *model);
|
||||
|
||||
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;
|
||||
NimBLEUUID m_uuid;
|
||||
|
||||
std::vector<NimBLEMeshElement*> m_elemVec;
|
||||
};
|
||||
|
||||
|
||||
#endif // CONFIG_BT_ENABLED
|
||||
#endif // MAIN_NIMBLE_MESH_NODE_H_
|
@ -54,6 +54,22 @@ int NimBLEUtils::checkConnParams(ble_gap_conn_params* params) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ble_npl_time_t NimBLEUtils::meshTransTimeMs(uint8_t tt) {
|
||||
switch(tt >> 6) {
|
||||
case 0:
|
||||
return 100;
|
||||
case 1:
|
||||
return 1000;
|
||||
case 2:
|
||||
return 10000;
|
||||
case 3:
|
||||
return 600000;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Converts a return code from the NimBLE stack to a text string.
|
||||
|
@ -44,6 +44,7 @@ public:
|
||||
static const char* advTypeToString(uint8_t advType);
|
||||
static const char* returnCodeToString(int rc);
|
||||
static int checkConnParams(ble_gap_conn_params* params);
|
||||
static ble_npl_time_t meshTransTimeMs(uint8_t tt);
|
||||
};
|
||||
|
||||
|
||||
|
295
src/mesh_config_store/base64/base64.h
Normal file
295
src/mesh_config_store/base64/base64.h
Normal file
@ -0,0 +1,295 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
#ifndef __UTIL_BASE64_H
|
||||
#define __UTIL_BASE64_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct base64_decoder {
|
||||
/*** public */
|
||||
const char *src;
|
||||
void *dst;
|
||||
int src_len; /* <=0 if src ends with '\0' */
|
||||
int dst_len; /* <=0 if dst unbounded */
|
||||
|
||||
/*** private */
|
||||
char buf[4];
|
||||
int buf_len;
|
||||
};
|
||||
|
||||
static const char base64_chars[] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
static int
|
||||
pos(char c)
|
||||
{
|
||||
const char *p;
|
||||
for (p = base64_chars; *p; p++)
|
||||
if (*p == c)
|
||||
return p - base64_chars;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
base64_encode(const void *data, int size, char *s, uint8_t should_pad)
|
||||
{
|
||||
char *p;
|
||||
int i;
|
||||
int c;
|
||||
const unsigned char *q;
|
||||
char *last;
|
||||
int diff;
|
||||
|
||||
p = s;
|
||||
|
||||
q = (const unsigned char *) data;
|
||||
last = NULL;
|
||||
i = 0;
|
||||
while (i < size) {
|
||||
c = q[i++];
|
||||
c *= 256;
|
||||
if (i < size)
|
||||
c += q[i];
|
||||
i++;
|
||||
c *= 256;
|
||||
if (i < size)
|
||||
c += q[i];
|
||||
i++;
|
||||
p[0] = base64_chars[(c & 0x00fc0000) >> 18];
|
||||
p[1] = base64_chars[(c & 0x0003f000) >> 12];
|
||||
p[2] = base64_chars[(c & 0x00000fc0) >> 6];
|
||||
p[3] = base64_chars[(c & 0x0000003f) >> 0];
|
||||
last = p;
|
||||
p += 4;
|
||||
}
|
||||
|
||||
if (last) {
|
||||
diff = i - size;
|
||||
if (diff > 0) {
|
||||
if (should_pad) {
|
||||
memset(last + (4 - diff), '=', diff);
|
||||
} else {
|
||||
p = last + (4 - diff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*p = 0;
|
||||
|
||||
return (p - s);
|
||||
}
|
||||
|
||||
int
|
||||
base64_pad(char *buf, int len)
|
||||
{
|
||||
int remainder;
|
||||
|
||||
remainder = len % 4;
|
||||
if (remainder == 0) {
|
||||
return (0);
|
||||
}
|
||||
|
||||
memset(buf, '=', 4 - remainder);
|
||||
|
||||
return (4 - remainder);
|
||||
}
|
||||
|
||||
#define DECODE_ERROR -1
|
||||
|
||||
static unsigned int
|
||||
token_decode(const char *token, int len)
|
||||
{
|
||||
int i;
|
||||
unsigned int val = 0;
|
||||
int marker = 0;
|
||||
|
||||
if (len < 4) {
|
||||
return DECODE_ERROR;
|
||||
}
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
val *= 64;
|
||||
if (token[i] == '=') {
|
||||
marker++;
|
||||
} else if (marker > 0) {
|
||||
return DECODE_ERROR;
|
||||
} else {
|
||||
val += pos(token[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (marker > 2) {
|
||||
return DECODE_ERROR;
|
||||
}
|
||||
|
||||
return (marker << 24) | val;
|
||||
}
|
||||
|
||||
int
|
||||
base64_decoder_go(struct base64_decoder *dec)
|
||||
{
|
||||
unsigned int marker;
|
||||
unsigned int val;
|
||||
uint8_t *dst;
|
||||
char sval;
|
||||
int read_len;
|
||||
int src_len;
|
||||
int src_rem;
|
||||
int src_off;
|
||||
int dst_len;
|
||||
int dst_off;
|
||||
int i;
|
||||
|
||||
dst = dec->dst;
|
||||
dst_off = 0;
|
||||
src_off = 0;
|
||||
|
||||
/* A length <= 0 means "unbounded". */
|
||||
if (dec->src_len <= 0) {
|
||||
src_len = INT_MAX;
|
||||
} else {
|
||||
src_len = dec->src_len;
|
||||
}
|
||||
if (dec->dst_len <= 0) {
|
||||
dst_len = INT_MAX;
|
||||
} else {
|
||||
dst_len = dec->dst_len;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
src_rem = src_len - src_off;
|
||||
if (src_rem == 0) {
|
||||
/* End of source input. */
|
||||
break;
|
||||
}
|
||||
|
||||
if (dec->src[src_off] == '\0') {
|
||||
/* End of source string. */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Account for possibility of partial token from previous call. */
|
||||
read_len = 4 - dec->buf_len;
|
||||
|
||||
/* Detect invalid input. */
|
||||
for (i = 0; i < read_len; i++) {
|
||||
sval = dec->src[src_off + i];
|
||||
if (sval == '\0') {
|
||||
/* Incomplete input. */
|
||||
return -1;
|
||||
}
|
||||
if (sval != '=' && strchr(base64_chars, sval) == NULL) {
|
||||
/* Invalid base64 character. */
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (src_rem < read_len) {
|
||||
/* Input contains a partial token. Stash it for use during the
|
||||
* next call.
|
||||
*/
|
||||
memcpy(&dec->buf[dec->buf_len], &dec->src[src_off], src_rem);
|
||||
dec->buf_len += src_rem;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Copy full token into buf and decode it. */
|
||||
memcpy(&dec->buf[dec->buf_len], &dec->src[src_off], read_len);
|
||||
val = token_decode(dec->buf, read_len);
|
||||
if (val == DECODE_ERROR) {
|
||||
return -1;
|
||||
}
|
||||
src_off += read_len;
|
||||
dec->buf_len = 0;
|
||||
|
||||
marker = (val >> 24) & 0xff;
|
||||
|
||||
if (dst_off >= dst_len) {
|
||||
break;
|
||||
}
|
||||
dst[dst_off] = (val >> 16) & 0xff;
|
||||
dst_off++;
|
||||
|
||||
if (marker < 2) {
|
||||
if (dst_off >= dst_len) {
|
||||
break;
|
||||
}
|
||||
dst[dst_off] = (val >> 8) & 0xff;
|
||||
dst_off++;
|
||||
}
|
||||
|
||||
if (marker < 1) {
|
||||
if (dst_off >= dst_len) {
|
||||
break;
|
||||
}
|
||||
dst[dst_off] = val & 0xff;
|
||||
dst_off++;
|
||||
}
|
||||
}
|
||||
|
||||
return dst_off;
|
||||
}
|
||||
|
||||
int
|
||||
base64_decode(const char *str, void *data)
|
||||
{
|
||||
struct base64_decoder dec = {
|
||||
.src = str,
|
||||
.dst = data,
|
||||
};
|
||||
|
||||
return base64_decoder_go(&dec);
|
||||
}
|
||||
|
||||
int
|
||||
base64_decode_maxlen(const char *str, void *data, int len)
|
||||
{
|
||||
struct base64_decoder dec = {
|
||||
.src = str,
|
||||
.dst = data,
|
||||
.dst_len = len,
|
||||
};
|
||||
|
||||
return base64_decoder_go(&dec);
|
||||
}
|
||||
|
||||
int
|
||||
base64_decode_len(const char *str)
|
||||
{
|
||||
int len;
|
||||
|
||||
len = strlen(str);
|
||||
while (len && str[len - 1] == '=') {
|
||||
len--;
|
||||
}
|
||||
return len * 3 / 4;
|
||||
}
|
||||
|
||||
#define BASE64_ENCODE_SIZE(__size) (((((__size) - 1) / 3) * 4) + 4)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __UTIL_BASE64_H__ */
|
238
src/mesh_config_store/config/config.h
Normal file
238
src/mesh_config_store/config/config.h
Normal file
@ -0,0 +1,238 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
#ifndef __SYS_CONFIG_H_
|
||||
#define __SYS_CONFIG_H_
|
||||
|
||||
#include "../../nimconfig.h"
|
||||
#if defined(CONFIG_NIMBLE_CPP_IDF)
|
||||
# include <os/queue.h>
|
||||
#else
|
||||
# include "nimble/porting/nimble/include/os/queue.h"
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define CONF_MAX_DIR_DEPTH 8 /* max depth of config tree */
|
||||
#define CONF_MAX_NAME_LEN (8 * CONF_MAX_DIR_DEPTH)
|
||||
|
||||
/**
|
||||
* Type of configuration value.
|
||||
*/
|
||||
typedef enum conf_type {
|
||||
CONF_NONE = 0,
|
||||
CONF_DIR,
|
||||
/** 8-bit signed integer */
|
||||
CONF_INT8,
|
||||
/** 16-bit signed integer */
|
||||
CONF_INT16,
|
||||
/** 32-bit signed integer */
|
||||
CONF_INT32,
|
||||
/** 64-bit signed integer */
|
||||
CONF_INT64,
|
||||
/** String */
|
||||
CONF_STRING,
|
||||
/** Bytes */
|
||||
CONF_BYTES,
|
||||
/** Floating point */
|
||||
CONF_FLOAT,
|
||||
/** Double precision */
|
||||
CONF_DOUBLE,
|
||||
/** Boolean */
|
||||
CONF_BOOL,
|
||||
/** 8-bit unsigned integer */
|
||||
CONF_UINT8,
|
||||
/** 16-bit unsigned integer */
|
||||
CONF_UINT16,
|
||||
/** 32-bit unsigned integer */
|
||||
CONF_UINT32,
|
||||
/** 64-bit unsigned integer */
|
||||
CONF_UINT64,
|
||||
} __attribute__((__packed__)) conf_type_t;
|
||||
|
||||
/**
|
||||
* Parameter to commit handler describing where data is going to.
|
||||
*/
|
||||
enum conf_export_tgt {
|
||||
/** Value is to be persisted */
|
||||
CONF_EXPORT_PERSIST,
|
||||
/** Value is to be display */
|
||||
CONF_EXPORT_SHOW
|
||||
};
|
||||
|
||||
typedef enum conf_export_tgt conf_export_tgt_t;
|
||||
|
||||
/**
|
||||
* Handler for getting configuration items, this handler is called
|
||||
* per-configuration section. Configuration sections are delimited
|
||||
* by '/', for example:
|
||||
*
|
||||
* - section/name/value
|
||||
*
|
||||
* Would be passed as:
|
||||
*
|
||||
* - argc = 3
|
||||
* - argv[0] = section
|
||||
* - argv[1] = name
|
||||
* - argv[2] = value
|
||||
*
|
||||
* The handler returns the value into val, null terminated, up to
|
||||
* val_len_max.
|
||||
*
|
||||
* @param argc The number of sections in the configuration variable
|
||||
* @param argv The array of configuration sections
|
||||
* @param val A pointer to the buffer to return the configuration
|
||||
* value into.
|
||||
* @param val_len_max The maximum length of the val buffer to copy into.
|
||||
*
|
||||
* @return A pointer to val or NULL if error.
|
||||
*/
|
||||
typedef char *(*conf_get_handler_t)(int argc, char **argv, char *val, int val_len_max);
|
||||
typedef char *(*conf_get_handler_ext_t)(int argc, char **argv, char *val, int val_len_max, void *arg);
|
||||
|
||||
/**
|
||||
* Set the configuration variable pointed to by argc and argv. See
|
||||
* description of ch_get_handler_t for format of these variables. This sets the
|
||||
* configuration variable to the shadow value, but does not apply the configuration
|
||||
* change. In order to apply the change, call the ch_commit() handler.
|
||||
*
|
||||
* @param argc The number of sections in the configuration variable.
|
||||
* @param argv The array of configuration sections
|
||||
* @param val The value to configure that variable to
|
||||
*
|
||||
* @return 0 on success, non-zero error code on failure.
|
||||
*/
|
||||
typedef int (*conf_set_handler_t)(int argc, char **argv, char *val);
|
||||
typedef int (*conf_set_handler_ext_t)(int argc, char **argv, char *val, void *arg);
|
||||
|
||||
/**
|
||||
* Commit shadow configuration state to the active configuration.
|
||||
*
|
||||
* @return 0 on success, non-zero error code on failure.
|
||||
*/
|
||||
typedef int (*conf_commit_handler_t)(void);
|
||||
typedef int (*conf_commit_handler_ext_t)(void *arg);
|
||||
|
||||
/**
|
||||
* Called per-configuration variable being exported.
|
||||
*
|
||||
* @param name The name of the variable to export
|
||||
* @param val The value of the variable to export
|
||||
*/
|
||||
typedef void (*conf_export_func_t)(char *name, char *val);
|
||||
|
||||
/**
|
||||
* Export all of the configuration variables, calling the export_func
|
||||
* per variable being exported.
|
||||
*
|
||||
* @param export_func The export function to call.
|
||||
* @param tgt The target of the export, either for persistence or display.
|
||||
*
|
||||
* @return 0 on success, non-zero error code on failure.
|
||||
*/
|
||||
typedef int (*conf_export_handler_t)(conf_export_func_t export_func,
|
||||
conf_export_tgt_t tgt);
|
||||
typedef int (*conf_export_handler_ext_t)(conf_export_func_t export_func,
|
||||
conf_export_tgt_t tgt, void *arg);
|
||||
|
||||
/**
|
||||
* Configuration handler, used to register a config item/subtree.
|
||||
*/
|
||||
struct conf_handler {
|
||||
SLIST_ENTRY(conf_handler) ch_list;
|
||||
/**
|
||||
* The name of the conifguration item/subtree
|
||||
*/
|
||||
char *ch_name;
|
||||
|
||||
/**
|
||||
* Whether to use the extended callbacks.
|
||||
* false: standard
|
||||
* true: extended
|
||||
*/
|
||||
bool ch_ext;
|
||||
|
||||
/** Get configuration value */
|
||||
union {
|
||||
conf_get_handler_t ch_get;
|
||||
conf_get_handler_ext_t ch_get_ext;
|
||||
};
|
||||
|
||||
/** Set configuration value */
|
||||
union {
|
||||
conf_set_handler_t ch_set;
|
||||
conf_set_handler_ext_t ch_set_ext;
|
||||
};
|
||||
|
||||
/** Commit configuration value */
|
||||
union {
|
||||
conf_commit_handler_t ch_commit;
|
||||
conf_commit_handler_ext_t ch_commit_ext;
|
||||
};
|
||||
|
||||
/** Export configuration value */
|
||||
union {
|
||||
conf_export_handler_t ch_export;
|
||||
conf_export_handler_ext_t ch_export_ext;
|
||||
};
|
||||
|
||||
/** Custom argument that gets passed to the extended callbacks */
|
||||
void *ch_arg;
|
||||
};
|
||||
|
||||
/**
|
||||
* Register a handler for configurations items.
|
||||
*
|
||||
* @param cf Structure containing registration info.
|
||||
*
|
||||
* @return 0 on success, non-zero on failure.
|
||||
*/
|
||||
int conf_register(struct conf_handler *cf);
|
||||
|
||||
/**
|
||||
* Load configuration from registered persistence sources. Handlers for
|
||||
* configuration subtrees registered earlier will be called for encountered
|
||||
* values.
|
||||
*
|
||||
* @return 0 on success, non-zero on failure.
|
||||
*/
|
||||
int conf_load(void);
|
||||
|
||||
/**
|
||||
* Write a single configuration value to persisted storage (if it has
|
||||
* changed value).
|
||||
*
|
||||
* @param name Name/key of the configuration item.
|
||||
* @param var Value of the configuration item.
|
||||
*
|
||||
* @return 0 on success, non-zero on failure.
|
||||
*/
|
||||
int conf_save_one(const char *name, char *var);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#define SYSINIT_PANIC_ASSERT_MSG(rc, msg) assert(rc)
|
||||
|
||||
#endif /* __SYS_CONFIG_H_ */
|
136
src/mesh_config_store/config/config_store.c
Normal file
136
src/mesh_config_store/config/config_store.c
Normal file
@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#include "nimconfig.h"
|
||||
#ifdef ESP_PLATFORM
|
||||
#if CONFIG_BT_NIMBLE_MESH && CONFIG_NIMBLE_CPP_PERSIST_MESH_SETTINGS
|
||||
|
||||
#include "config.h"
|
||||
#include "nvs.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
static struct conf_handler* config_handler;
|
||||
|
||||
int conf_parse_name(char *name, int *name_argc, char *name_argv[])
|
||||
{
|
||||
char *tok;
|
||||
char *tok_ptr;
|
||||
const char *sep = "/";
|
||||
int i;
|
||||
|
||||
tok = strtok_r(name, sep, &tok_ptr);
|
||||
|
||||
i = 0;
|
||||
while (tok) {
|
||||
name_argv[i++] = tok;
|
||||
tok = strtok_r(NULL, sep, &tok_ptr);
|
||||
}
|
||||
*name_argc = i;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int conf_load(void)
|
||||
{
|
||||
esp_err_t err;
|
||||
nvs_handle handle;
|
||||
|
||||
err = nvs_open(config_handler->ch_name, NVS_READONLY, &handle);
|
||||
if (err != ESP_OK) return err;
|
||||
|
||||
nvs_iterator_t it = nvs_entry_find("nvs", config_handler->ch_name, NVS_TYPE_ANY);
|
||||
|
||||
while (it != NULL) {
|
||||
nvs_entry_info_t info;
|
||||
nvs_entry_info(it, &info);
|
||||
it = nvs_entry_next(it);
|
||||
|
||||
size_t required_size = 0;
|
||||
err = nvs_get_str(handle, info.key, NULL, &required_size);
|
||||
if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) return err;
|
||||
|
||||
char* val = malloc(required_size);
|
||||
if (required_size > 0) {
|
||||
err = nvs_get_str(handle, info.key, val, &required_size);
|
||||
if (err != ESP_OK) {
|
||||
free(val);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
int name_argc;
|
||||
char *name_argv[8];
|
||||
conf_parse_name(info.key, &name_argc, name_argv);
|
||||
|
||||
config_handler->ch_set(name_argc, &name_argv[0], val);
|
||||
free(val);
|
||||
}
|
||||
|
||||
nvs_close(handle);
|
||||
config_handler->ch_commit();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
int conf_save_one(const char *name, char *var)
|
||||
{
|
||||
esp_err_t err;
|
||||
nvs_handle_t handle;
|
||||
int name_argc;
|
||||
char *name_argv[CONF_MAX_DIR_DEPTH];
|
||||
char n[CONF_MAX_NAME_LEN];
|
||||
|
||||
strcpy(n, name);
|
||||
conf_parse_name(n, &name_argc, name_argv);
|
||||
|
||||
err = nvs_open(name_argv[0], NVS_READWRITE, &handle);
|
||||
if (err != ESP_OK) return err;
|
||||
|
||||
const char* key = name_argv[1];
|
||||
if (name_argc > 2) {
|
||||
key = name;
|
||||
while (*key != '/') {
|
||||
key++;
|
||||
}
|
||||
key++;
|
||||
}
|
||||
|
||||
if (var) {
|
||||
err = nvs_set_str(handle, key, var);
|
||||
if (err != ESP_OK) return err;
|
||||
} else {
|
||||
err = nvs_erase_key(handle, key);
|
||||
if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) return err;
|
||||
}
|
||||
|
||||
err = nvs_commit(handle);
|
||||
if (err != ESP_OK) return err;
|
||||
|
||||
nvs_close(handle);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
int conf_register(struct conf_handler *cf)
|
||||
{
|
||||
config_handler = cf;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // CONFIG_BT_NIMBLE_MESH && MYNEWT_VAL_BLE_MESH_SETTINGS
|
||||
#endif // ESP_PLATFORM
|
@ -131,6 +131,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
|
||||
|
||||
|
Reference in New Issue
Block a user