Table of Contents
- Overview
- Architecture
- Writing Custom Models
- Writing Custom Elements
- Complete Example
- Best Practices
- Common Patterns
Overview
This guide provides comprehensive instructions for developers on how to create custom model and element implementations in the MeshX BLE mesh framework. The MeshX framework uses a layered architecture with clear separation between models (which handle BLE mesh protocol logic) and elements (which manage state and coordinate multiple models).
Key Concepts
- Models: Handle BLE mesh protocol-specific operations, message processing, and communication with the BLE stack
- Elements: Manage state, coordinate multiple models, and provide persistence (NVS) for device state
- Server Models: Respond to requests from other devices in the mesh network
- Client Models: Initiate requests to other devices in the mesh network
Architecture
Model Class Hierarchy
meshXModelIF (interface)
└── meshXModel (base template)
├── meshXServerModel (for server models)
│ └── YourCustomServerModel
└── meshXClientModel (for client models)
└── YourCustomClientModel
Element Class Hierarchy
meshXElementIF (interface)
└── meshXElement (base template)
├── meshXElementServer (for server elements)
│ └── YourCustomServerElement
└── meshXElementClient (for client elements)
└── YourCustomClientElement
Base Model Integration
Models also integrate with platform-specific base model classes:
meshXBaseModel (platform abstraction)
├── meshXBaseServerModel
│ └── meshXBaseGenericServerModel / meshXBaseLightServerModel
└── meshXBaseClientModel
└── meshXBaseGenericClientModel / meshXBaseLightClientModel
Writing Custom Models
Model Class Hierarchy
All custom models must inherit from either meshXServerModel or meshXClientModel, which in turn inherit from meshXModel.
Server Model Implementation
Step 1: Define the Header File (.hpp)
Create a header file that defines your model class, state structure, and message structure.
#ifndef _MESHX_MODEL_CUSTOM_HPP_
#define _MESHX_MODEL_CUSTOM_HPP_
#define MESHX_CUSTOM_SERVER_MODEL_TEMPLATE_PROTO
#define MESHX_CUSTOM_SERVER_MODEL_TEMPLATE_PARAMS
struct meshx_custom_model_state
{
uint8_t value1;
uint16_t value2;
};
using meshx_custom_model_state_t = struct meshx_custom_model_state;
struct meshx_custom_send_params
{
meshx_custom_model_state_t
state;
};
using meshx_custom_send_params_t = struct meshx_custom_send_params;
struct meshx_custom_srv_el_msg
{
meshx_custom_model_state_t
state;
};
using meshx_custom_srv_el_msg_t = struct meshx_custom_srv_el_msg;
MESHX_CUSTOM_SERVER_MODEL_TEMPLATE_PROTO
class meshXCustomServerModel :
public meshXServerModel<meshXBaseGenericServerModel, meshx_custom_send_params_t>
{
private:
meshx_custom_model_state_t model_state;
meshx_custom_srv_el_msg_t element_msg;
bool element_msg_prepared;
public:
explicit meshXCustomServerModel(
meshXElementIF *parent_element = nullptr,
);
~meshXCustomServerModel() override = default;
};
#endif
virtual meshx_err_t prepare_element_msg(meshx_ptr_t *msg_ptr, size_t *msg_size)=0
Prepare message for element notification.
virtual meshx_err_t plat_model_create(MESHX_MODEL *p_plat_model_ptr=nullptr)=0
Create logical model instance.
virtual meshx_err_t plat_model_delete(void)=0
Delete logical model instance.
virtual meshx_err_t element_state_change_handle(void)=0
Handle state change request from element.
virtual meshx_err_t model_from_ble_cb(dev_struct_t *dev, control_task_msg_evt_t evt, meshx_ptr_t data)=0
virtual meshx_err_t model_send(meshx_send_packet_params_t *params)=0
Base class for all server models in MeshX.
Definition meshx_model_class.hpp:366
Header file for MeshX Generic Client and Server model declarations.
struct meshx_model meshx_model_t
struct meshx_ctx meshx_ctx_t
Structure to hold context information for BLE Mesh operations.
void * meshx_ptr_t
Definition meshx_ble_mesh_cmn_def.h:636
struct dev_struct dev_struct_t
Structure representing the device composition and elements.
uint32_t control_task_msg_evt_t
Type definition for control task message event.
Definition meshx_control_task.h:81
meshx_err_t
MeshX Error Codes.
Definition meshx_err.h:43
Template declarations for MeshX model wrapper classes.
struct meshx_srv_model_send_param_header meshx_srv_model_send_param_header_t
Definition meshx_model_class.hpp:354
uint8_t state
Definition meshx_serial.c:39
Step 2: Implement the Source File (.cpp)
#include <generic_model/meshx_model_custom.hpp>
MESHX_CUSTOM_SERVER_MODEL_TEMPLATE_PROTO
meshXCustomServerModel MESHX_CUSTOM_SERVER_MODEL_TEMPLATE_PARAMS
:
meshXServerModel(nullptr, YOUR_MODEL_ID, parent_element, parent_element_state)
{
model_state.value1 = 0;
model_state.value2 = 0;
element_msg_prepared = false;
}
MESHX_CUSTOM_SERVER_MODEL_TEMPLATE_PROTO
meshx_err_t meshXCustomServerModel MESHX_CUSTOM_SERVER_MODEL_TEMPLATE_PARAMS
::plat_model_create(void)
{
err = meshx_plat_custom_srv_create(this->get_plat_model(), &p_pub, &p_gen);
if(err)
{
}
else
{
this->set_pub_struct(p_pub);
this->set_gen_struct(p_gen);
}
return err;
}
MESHX_CUSTOM_SERVER_MODEL_TEMPLATE_PROTO
meshx_err_t meshXCustomServerModel MESHX_CUSTOM_SERVER_MODEL_TEMPLATE_PARAMS
::plat_model_delete(void)
{
meshx_err_t err = meshx_plat_custom_srv_delete(&p_pub, &p_gen);
if (err)
{
}
else
{
this->set_pub_struct(nullptr);
this->set_gen_struct(nullptr);
}
return err;
}
MESHX_CUSTOM_SERVER_MODEL_TEMPLATE_PROTO
meshx_err_t meshXCustomServerModel MESHX_CUSTOM_SERVER_MODEL_TEMPLATE_PARAMS
::model_from_ble_cb(
{
if(!params || !p_dev)
{
}
if(model_id != this->get_model_id())
{
}
param->ctx.opcode, param->ctx.src_addr, param->ctx.dst_addr);
model_state.value1 = param->state_change.custom_set.value1;
model_state.value2 = param->state_change.custom_set.value2;
element_msg_prepared = false;
bool send_reply = (param->ctx.opcode != YOUR_OPCODE_SET_UNACK);
switch (param->ctx.opcode)
{
case YOUR_OPCODE_GET:
case YOUR_OPCODE_SET:
case YOUR_OPCODE_SET_UNACK:
{
{
element_msg = {
.header = {
.model = param->model,
},
.state = model_state,
};
element_msg_prepared = true;
}
break;
}
default:
}
if (send_reply
|| param->ctx.src_addr != param->model.pub_addr)
{
param->ctx.dst_addr = param->model.pub_addr;
meshx_custom_send_params_t send_params = {
.model = ¶m->model,
.ctx = ¶m->ctx,
.state = model_state,
};
return this->model_send(&send_params);
}
if (!element_msg_prepared)
{
}
}
MESHX_CUSTOM_SERVER_MODEL_TEMPLATE_PROTO
meshx_err_t meshXCustomServerModel MESHX_CUSTOM_SERVER_MODEL_TEMPLATE_PARAMS
::element_state_change_handle()
{
auto *el_state = static_cast<meshx_custom_model_state_t*>(this->get_parent_element_state());
if (!el_state) {
}
if(el_state->value1 != model_state.value1 || el_state->value2 != model_state.value2)
{
el_state->value1 = model_state.value1;
el_state->value2 = model_state.value2;
}
else
{
}
}
MESHX_CUSTOM_SERVER_MODEL_TEMPLATE_PROTO
meshx_err_t meshXCustomServerModel MESHX_CUSTOM_SERVER_MODEL_TEMPLATE_PARAMS
::prepare_element_msg(
meshx_ptr_t *msg_ptr,
size_t *msg_size)
{
if (!msg_ptr || !msg_size)
{
}
if (!element_msg_prepared)
{
}
*msg_ptr = &element_msg;
*msg_size = sizeof(element_msg);
}
MESHX_CUSTOM_SERVER_MODEL_TEMPLATE_PROTO
meshx_err_t meshXCustomServerModel MESHX_CUSTOM_SERVER_MODEL_TEMPLATE_PARAMS
::model_send(meshx_custom_send_params_t *params)
{
if (!params || !params->model || !params->ctx)
{
}
params->ctx->opcode = YOUR_OPCODE_STATUS;
.p_model = params->model,
.p_ctx = params->ctx,
.state_change = {
.custom_set = {
.value1 = params->state.value1,
.value2 = params->state.value2,
}
},
.data_len = sizeof(meshx_state_change_custom_set_t)
};
return this->get_base_model()->plat_send_msg(&send_params);
}
Interface class for MeshX elements.
Definition meshx_fwd_decl.hpp:82
struct meshx_gen_server_send_params { meshx_model_t *p_model; meshx_ctx_t *p_ctx; meshx_gen_srv_state_change_t state_change; size_t data_len; } meshx_gen_server_send_params_t
Definition meshx_base_model_generic.hpp:135
meshx_err_t meshx_is_group_subscribed(meshx_model_t *p_model, uint16_t addr)
Checks if a model is subscribed to a specific group address.
#define MESHX_ADDR_IS_GROUP(_addr)
Definition meshx_ble_mesh_cmn_def.h:27
#define MESHX_ADDR_IS_UNICAST(_addr)
Definition meshx_ble_mesh_cmn_def.h:25
#define MESHX_ADDR_BROADCAST(_addr)
Definition meshx_ble_mesh_cmn_def.h:26
struct meshx_gen_srv_cb_param meshx_gen_srv_cb_param_t
@ MESHX_INVALID_ARG
Definition meshx_err.h:46
@ MESHX_NOT_SUPPORTED
Definition meshx_err.h:51
@ MESHX_INVALID_STATE
Definition meshx_err.h:49
#define MESHX_LOGE(module_id, format,...)
Definition meshx_log.h:114
#define MESHX_LOGD(module_id, format,...)
Definition meshx_log.h:132
**This function is called by the parent element when a state change request *is received It validates the request and returns a result to the element not the model layer **return * MESHX_SUCCESS
Definition meshx_model_level.cpp:385
@ MODULE_ID_MODEL_SERVER
Definition module_id.h:34
Client Model Implementation
Client models follow a similar pattern but with some key differences:
- They inherit from meshXClientModel
- They don't implement plat_model_create() and plat_model_delete() (these are final in the base class)
- They use a different message header structure (meshx_cli_model_send_param_header_t)
- They handle timeout events
MESHX_CUSTOM_CLIENT_MODEL_TEMPLATE_PROTO
class meshXCustomClientModel :
public meshXClientModel<meshXBaseGenericClientModel, meshx_custom_send_params_t>
{
private:
meshx_custom_model_state_t model_state;
meshx_custom_cli_el_msg_t element_msg;
public:
explicit meshXCustomClientModel(
);
~meshXCustomClientModel() override = default;
};
Base class for all client models in the mesh network.
Definition meshx_model_class.hpp:429
struct meshx_gen_cli_cb_param meshx_gen_cli_cb_param_t
Callback parameters for Generic Client Model events. This structure is used to pass information about...
Key Virtual Functions
All models must implement these virtual functions from meshXModelIF:
1. plat_model_create() (Server only)
Creates the platform-specific model instance. Call the platform-specific creation function and set the publication and generic structures.
2. plat_model_delete() (Server only)
Deletes the platform-specific model instance and cleans up resources.
3. element_state_change_handle()
Handles state change requests from the parent element. Update the element state if it has changed.
4. prepare_element_msg()
Prepares a message structure that will be sent to the parent element. The message must persist after this function returns (store as member variable).
5. model_from_ble_cb()
Handles messages and events coming from the BLE Mesh network. Process the received message, update model state, and prepare element notification if needed.
6. model_send()
Sends messages through the model to the BLE Mesh network. Prepare the send parameters and call the base model's plat_send_msg().
Writing Custom Elements
Element Class Hierarchy
All custom elements must inherit from either meshXElementServer or meshXElementClient.
Server Element Implementation
Step 1: Define the Header File (.hpp)
#ifndef __MESHX_CUSTOM_ELEMENT_HPP__
#define __MESHX_CUSTOM_ELEMENT_HPP__
#include <generic_model/meshx_model_custom.hpp>
#define MESHX_CUSTOM_SERVER_ELEMENT_TEMPLATE_PROTO
#define MESHX_CUSTOM_SERVER_ELEMENT_TEMPLATE_PARAMS
#if CONFIG_CUSTOM_SERVER_COUNT > 0
struct meshx_custom_srv_el_ctx_t
{
uint8_t app_id;
uint16_t pub_addr;
meshx_custom_model_state_t custom_state;
};
using meshx_custom_srv_el_ctx_t = struct meshx_custom_srv_el_ctx_t;
MESHX_CUSTOM_SERVER_ELEMENT_TEMPLATE_PROTO
class meshXCustomServerElement :
public meshXElementServer MESHX_CUSTOM_SERVER_ELEMENT_TEMPLATE_PARAMS
{
private:
meshx_custom_srv_el_ctx_t element_ctx;
public:
meshXCustomServerElement(uint16_t element_idx);
meshXCustomServerElement(void) = delete;
};
#endif
#endif
Derived class for server elements.
Definition meshx_element_class.hpp:336
virtual uint8_t list_sig_models(void)
Definition meshx_element_class.hpp:94
virtual uint8_t list_ven_models(void)
Definition meshx_element_class.hpp:106
MeshX Element class and interface declaration This file contains the meshXElement class and its inter...
Step 2: Implement the Source File (.cpp)
#include <variants/meshx_custom_element.hpp>
#if CONFIG_CUSTOM_SERVER_COUNT > 0
MESHX_CUSTOM_SERVER_ELEMENT_TEMPLATE_PROTO
meshXCustomServerElement MESHX_CUSTOM_SERVER_ELEMENT_TEMPLATE_PARAMS
::meshXCustomServerElement(uint16_t element_idx)
{
this->register_element_ctx(
&element_ctx,
sizeof(meshx_custom_srv_el_ctx_t)
);
}
MESHX_CUSTOM_SERVER_ELEMENT_TEMPLATE_PROTO
uint8_t meshXCustomServerElement MESHX_CUSTOM_SERVER_ELEMENT_TEMPLATE_PARAMS
::list_sig_models()
{
auto custom_model = std::make_unique<meshXCustomServerModel>(
this,
&this->element_ctx.custom_state
);
this->get_sig_models().push_back(std::move(custom_model));
return (uint8_t)this->get_sig_models().size();
}
MESHX_CUSTOM_SERVER_ELEMENT_TEMPLATE_PROTO
uint8_t meshXCustomServerElement MESHX_CUSTOM_SERVER_ELEMENT_TEMPLATE_PARAMS
::list_ven_models()
{
return 0;
}
#endif
Client Element Implementation
Client elements follow the same pattern but inherit from meshXElementClient:
MESHX_CUSTOM_CLIENT_ELEMENT_TEMPLATE_PROTO
class meshXCustomClientElement :
public meshXElementClient MESHX_CUSTOM_CLIENT_ELEMENT_TEMPLATE_PARAMS
{
private:
meshx_custom_cli_el_ctx_t element_ctx;
public:
meshXCustomClientElement(uint16_t element_idx);
meshXCustomClientElement(void) = delete;
};
Derived class for client elements.
Definition meshx_element_class.hpp:349
Element Context and State Management
Elements maintain state in a context structure that is persisted to NVS (Non-Volatile Storage). This ensures that device state survives power cycles.
Key Points:
- Context Structure: Define a context structure that contains all model states and publication/app binding information
- Registration: Call register_element_ctx() in the constructor to register the context
- Model State Pointers: Pass pointers to individual model states when creating models
- NVS Persistence: The framework automatically saves/restores the context structure to/from NVS
Example from meshx_relay_element.hpp:
{
};
struct meshx_gen_onoff_model_state meshx_gen_onoff_model_state_t
Definition meshx_model_onoff.hpp:38
Relay server element context structure.
Definition meshx_relay_element.hpp:32
uint16_t pub_addr
Definition meshx_relay_element.hpp:34
uint16_t app_id
Definition meshx_relay_element.hpp:33
meshx_gen_onoff_model_state_t gen_on_off_state
Definition meshx_relay_element.hpp:35
Complete Example
Let's walk through a complete example of creating a custom "Power Level" model and element.
Step 1: Define Model Header
#ifndef _MESHX_MODEL_POWER_LEVEL_HPP_
#define _MESHX_MODEL_POWER_LEVEL_HPP_
#define MESHX_POWER_LEVEL_SERVER_MODEL_TEMPLATE_PROTO
#define MESHX_POWER_LEVEL_SERVER_MODEL_TEMPLATE_PARAMS
struct meshx_power_level_model_state
{
uint16_t power;
uint16_t last_power;
};
using meshx_power_level_model_state_t = struct meshx_power_level_model_state;
struct meshx_power_level_send_params
{
meshx_power_level_model_state_t
state;
};
using meshx_power_level_send_params_t = struct meshx_power_level_send_params;
{
meshx_power_level_model_state_t
state;
};
MESHX_POWER_LEVEL_SERVER_MODEL_TEMPLATE_PROTO
class meshXPowerLevelServerModel :
public meshXServerModel<meshXBaseGenericServerModel, meshx_power_level_send_params_t>
{
private:
meshx_power_level_model_state_t model_state;
bool element_msg_prepared;
public:
explicit meshXPowerLevelServerModel(
meshXElementIF *parent_element = nullptr,
);
~meshXPowerLevelServerModel() override = default;
};
#endif
struct meshx_power_level_srv_el_msg meshx_power_level_srv_el_msg_t
Definition meshx_model_power_level.hpp:129
Structure to hold the Power Level Server to parent element message. The structure is used by the on_m...
Definition meshx_model_power_level.hpp:124
meshx_gen_power_level_model_state_t state
Definition meshx_model_power_level.hpp:126
meshx_srv_model_send_param_header_t header
Definition meshx_model_power_level.hpp:125
Step 2: Implement Model
MESHX_POWER_LEVEL_SERVER_MODEL_TEMPLATE_PROTO
meshXPowerLevelServerModel MESHX_POWER_LEVEL_SERVER_MODEL_TEMPLATE_PARAMS
{
model_state.power = 0;
model_state.last_power = 0;
element_msg_prepared = false;
}
MESHX_POWER_LEVEL_SERVER_MODEL_TEMPLATE_PROTO
meshx_err_t meshXPowerLevelServerModel MESHX_POWER_LEVEL_SERVER_MODEL_TEMPLATE_PARAMS
::plat_model_create(void)
{
err = meshx_plat_power_level_srv_create(this->get_plat_model(), &p_pub, &p_gen);
if(err)
{
}
else
{
this->set_pub_struct(p_pub);
this->set_gen_struct(p_gen);
}
return err;
}
MESHX_POWER_LEVEL_SERVER_MODEL_TEMPLATE_PROTO
meshx_err_t meshXPowerLevelServerModel MESHX_POWER_LEVEL_SERVER_MODEL_TEMPLATE_PARAMS
::plat_model_delete(void)
{
meshx_err_t err = meshx_plat_power_level_srv_delete(&p_pub, &p_gen);
if (err)
{
}
else
{
this->set_pub_struct(nullptr);
this->set_gen_struct(nullptr);
}
return err;
}
MESHX_POWER_LEVEL_SERVER_MODEL_TEMPLATE_PROTO
meshx_err_t meshXPowerLevelServerModel MESHX_POWER_LEVEL_SERVER_MODEL_TEMPLATE_PARAMS
::model_from_ble_cb(
{
if(!params || !p_dev)
{
}
if(model_id != this->get_model_id())
{
}
param->ctx.opcode, param->ctx.src_addr, param->ctx.dst_addr);
model_state.power = param->state_change.power_level_set.power;
element_msg_prepared = false;
switch (param->ctx.opcode)
{
{
{
element_msg = {
.header = {
.model = param->model,
},
.state = model_state,
};
element_msg_prepared = true;
}
break;
}
default:
}
if (send_reply || param->ctx.src_addr != param->model.pub_addr)
{
param->ctx.dst_addr = param->model.pub_addr;
meshx_power_level_send_params_t send_params = {
.model = ¶m->model,
.ctx = ¶m->ctx,
.state = model_state,
};
return this->model_send(&send_params);
}
if (!element_msg_prepared)
{
}
}
MESHX_POWER_LEVEL_SERVER_MODEL_TEMPLATE_PROTO
meshx_err_t meshXPowerLevelServerModel MESHX_POWER_LEVEL_SERVER_MODEL_TEMPLATE_PARAMS
::element_state_change_handle()
{
auto *el_state = static_cast<meshx_power_level_model_state_t*>(this->get_parent_element_state());
if (!el_state) {
}
if(el_state->power != model_state.power)
{
el_state->power = model_state.power;
}
else
{
}
}
MESHX_POWER_LEVEL_SERVER_MODEL_TEMPLATE_PROTO
meshx_err_t meshXPowerLevelServerModel MESHX_POWER_LEVEL_SERVER_MODEL_TEMPLATE_PARAMS
::prepare_element_msg(
meshx_ptr_t *msg_ptr,
size_t *msg_size)
{
if (!msg_ptr || !msg_size)
{
}
if (!element_msg_prepared)
{
}
*msg_ptr = &element_msg;
*msg_size = sizeof(element_msg);
}
MESHX_POWER_LEVEL_SERVER_MODEL_TEMPLATE_PROTO
meshx_err_t meshXPowerLevelServerModel MESHX_POWER_LEVEL_SERVER_MODEL_TEMPLATE_PARAMS
::model_send(meshx_power_level_send_params_t *params)
{
if (!params || !params->model || !params->ctx)
{
}
.p_model = params->model,
.p_ctx = params->ctx,
.state_change = {
.power_level_set = {
.power = params->state.power,
}
},
.data_len = sizeof(meshx_state_change_power_level_set_t)
};
return this->get_base_model()->plat_send_msg(&send_params);
}
#define MESHX_MODEL_OP_GEN_POWER_LEVEL_GET
Definition meshx_ble_mesh_cmn_def.h:271
#define MESHX_MODEL_OP_GEN_POWER_LEVEL_SET_UNACK
Definition meshx_ble_mesh_cmn_def.h:273
#define MESHX_MODEL_ID_GEN_POWER_LEVEL_SRV
Definition meshx_ble_mesh_cmn_def.h:79
#define MESHX_MODEL_OP_GEN_POWER_LEVEL_STATUS
Definition meshx_ble_mesh_cmn_def.h:274
#define MESHX_MODEL_OP_GEN_POWER_LEVEL_SET
Definition meshx_ble_mesh_cmn_def.h:272
Implementation of Generic Power Level Model for MeshX.
Step 3: Define Element Header
#ifndef __MESHX_POWER_ELEMENT_HPP__
#define __MESHX_POWER_ELEMENT_HPP__
#define MESHX_POWER_SERVER_ELEMENT_TEMPLATE_PROTO
#define MESHX_POWER_SERVER_ELEMENT_TEMPLATE_PARAMS
#if CONFIG_POWER_SERVER_COUNT > 0
struct meshx_power_srv_el_ctx_t
{
uint8_t app_id;
uint16_t pub_addr;
meshx_power_level_model_state_t power_level_state;
};
using meshx_power_srv_el_ctx_t = struct meshx_power_srv_el_ctx_t;
MESHX_POWER_SERVER_ELEMENT_TEMPLATE_PROTO
class meshXPowerServerElement :
public meshXElementServer MESHX_POWER_SERVER_ELEMENT_TEMPLATE_PARAMS
{
private:
meshx_power_srv_el_ctx_t element_ctx;
public:
meshXPowerServerElement(uint16_t element_idx);
meshXPowerServerElement(void) = delete;
};
#endif
#endif
Step 4: Implement Element
#include <variants/meshx_power_element.hpp>
#if CONFIG_POWER_SERVER_COUNT > 0
MESHX_POWER_SERVER_ELEMENT_TEMPLATE_PROTO
meshXPowerServerElement MESHX_POWER_SERVER_ELEMENT_TEMPLATE_PARAMS
::meshXPowerServerElement(uint16_t element_idx)
{
this->register_element_ctx(
&element_ctx,
sizeof(meshx_power_srv_el_ctx_t)
);
}
MESHX_POWER_SERVER_ELEMENT_TEMPLATE_PROTO
uint8_t meshXPowerServerElement MESHX_POWER_SERVER_ELEMENT_TEMPLATE_PARAMS
::list_sig_models()
{
auto power_model = std::make_unique<meshXPowerLevelServerModel>(
this,
&this->element_ctx.power_level_state
);
this->get_sig_models().push_back(std::move(power_model));
return (uint8_t)this->get_sig_models().size();
}
MESHX_POWER_SERVER_ELEMENT_TEMPLATE_PROTO
uint8_t meshXPowerServerElement MESHX_POWER_SERVER_ELEMENT_TEMPLATE_PARAMS
::list_ven_models()
{
return 0;
}
#endif
Best Practices
1. Naming Conventions
- Model Classes: Prefix with meshX, suffix with Model (e.g., meshXCustomModel)
- Element Classes: Prefix with meshX, suffix with Element (e.g., meshXCustomElement)
- State Structures: Use descriptive names with _state_t suffix (e.g., meshx_custom_model_state_t)
- Message Structures: Use descriptive names with _el_msg_t suffix (e.g., meshx_custom_srv_el_msg_t)
- Context Structures: Use descriptive names with _el_ctx_t suffix (e.g., meshx_custom_srv_el_ctx_t)
2. Error Handling
- Always validate input parameters
- Use appropriate error codes from meshx_err.h
- Log errors using MESHX_LOGE macro
- Return error codes consistently
3. Memory Management
- Store element messages as member variables to ensure persistence
- Use std::unique_ptr for model ownership
- The framework handles model lifecycle management
4. State Management
- Keep model state separate from element state
- Update element state only in element_state_change_handle()
- Use the element context for NVS persistence
5. Message Handling
- Always check if the message is for this model (compare model IDs)
- Handle GET, SET, and SET_UNACK opcodes appropriately
- Prepare element messages only when necessary
- Use the element_msg_prepared flag to track message preparation
6. Documentation
- Add comprehensive Doxygen comments to all public functions
- Document all structure members
- Include usage examples in header files
- Maintain consistent formatting
Common Patterns
Pattern 1: Simple State Model
For models with a single state value (like OnOff):
struct meshx_simple_model_state
{
uint8_t value;
};
Pattern 2: Multi-State Model
For models with multiple related state values (like CTL):
struct meshx_multi_state_model_state
{
uint16_t value1;
uint16_t value2;
int16_t value3;
};
Pattern 3: Element with Multiple Models
For elements that combine multiple models (like CWWW):
struct meshx_multi_model_el_ctx_t
{
uint8_t app_id;
uint16_t pub_addr;
meshx_model1_state_t model1_state;
meshx_model2_state_t model2_state;
};
uint8_t list_sig_models() override
{
auto model1 = std::make_unique<meshXModel1ServerModel>(this, &element_ctx.model1_state);
this->get_sig_models().push_back(std::move(model1));
auto model2 = std::make_unique<meshXModel2ServerModel>(this, &element_ctx.model2_state);
this->get_sig_models().push_back(std::move(model2));
return (uint8_t)this->get_sig_models().size();
}
Pattern 4: Conditional Element Notification
Only notify the element when state actually changes:
if(el_state->value != model_state.value)
{
el_state->value = model_state.value;
}
else
{
}
Pattern 5: Opcode Switch Statement
Handle different opcodes in model_from_ble_cb():
switch (param->ctx.opcode)
{
case YOUR_OPCODE_GET:
case YOUR_OPCODE_SET:
case YOUR_OPCODE_SET_UNACK:
model_state.value = param->state_change.value;
element_msg = { };
element_msg_prepared = true;
break;
default:
}
Additional Resources
Reference Implementations
Base Classes
Platform Base Models
Conclusion
This guide provides a comprehensive overview of how to create custom models and elements in the MeshX framework. By following the patterns and best practices outlined here, you can extend the framework to support new BLE mesh models and create custom elements that meet your specific requirements.
Remember to:
- Study the existing reference implementations
- Follow the established naming conventions
- Implement all required virtual functions
- Handle errors appropriately
- Document your code thoroughly
- Test thoroughly with the BLE mesh network
For questions or issues, refer to the existing implementations in the codebase or consult the MeshX development team.