Hi DevZone!
I'm currently developing a device that has several hardware actuators and I've implemented 2 OnOff Models each belonging to an element. But right now I'm having trouble in the nRF Mesh app when it requests composition data, during this process the device log is shown in the image below.
My question is, can the device have several of the same model IDs like OnOff, Sensors server, and others? Is necessary a Kconfig configuration I'm missing to do it? I'm attaching here my model_handler.c and prj.conf to check if everything is fine. Also to mention, in this device, I have a custom battery model that is already working as I want to.
I'm working on NCS v2.5.0 and two 52840 DK's ( 1 for the server models and 1 for the client models).
Best Regards, Caio.
#include <zephyr/bluetooth/bluetooth.h> #include <bluetooth/mesh/models.h> #include <dk_buttons_and_leds.h> #include <float.h> #include "model_handler.h" #if defined(CONFIG_LOG) #include <zephyr/logging/log.h> #endif LOG_MODULE_REGISTER(model_handler); /*Company ID - Defined in prj.conf, TODO: Each vendor must have a CID registered in Bluetooth SIG, using Nordic CID for convinience here */ /* Creating custom model for server */ #define MY_BATTERY_MODEL_ID 0x0000 /*Generic Battery Server*/ //OPCODES for battery #define BATTERY_GET_OP BT_MESH_MODEL_OP_3(0x01, CONFIG_BT_COMPANY_ID) //GET (3 bytes for the opcode and 2 bytes for the voltage #define BATTERY_TYPE_SET_UNACK_OP BT_MESH_MODEL_OP_3(0x02, CONFIG_BT_COMPANY_ID) // SET (3 bytes for the opcode and 1 byte for the type) #define BATTERY_STATUS_OP BT_MESH_MODEL_OP_3(0x03, CONFIG_BT_COMPANY_ID) // STATUS OP MESSAGE, RESPONSE FROM GET MESSAGE. (3 bytes for the opcode and 1 byte for the type) //OPCODES for auto_lock #define LOCK_GET_OP BT_MESH_MODEL_OP_2(0x82, 0x01) #define LOCK_SET_UNACK_OP BT_MESH_MODEL_OP_2(0x82, 0x02) #define LOCK_STATUS_OP BT_MESH_MODEL_OP_3(0x82, 0x03) //OPCODES for auto_buzzer #define BUZZER_GET_OP BT_MESH_MODEL_OP_2(0X82, 0x04) #define BUZZER_SET_UNACK_OP BT_MESH_MODEL_OP_2(0x82, 0x05) #define BUZZER_STATUS_OP BT_MESH_MODEL_OP_2(0x82, 0x06) //OPCODES for auto_light #define LIGHT_GET_OP BT_MESH_MODEL_OP_2(0x82, 0x07) #define LIGHT_SET_UNACK_OP BT_MESH_MODEL_OP_2(0x82, 0x08) #define LIGHT_STATUS_OP BT_MESH_MODEL_OP_2(0x82, 0x09) //Server model publication context: //Battery BT_MESH_MODEL_PUB_DEFINE(battery_srv_pub, NULL, 2 + 1); //2 bytes for the voltage and 1 byte for the type //Lock BT_MESH_MODEL_PUB_DEFINE(lock_srv_pub, NULL, 1 + 2); //1 Byte for the On-Off byte and 2 bytes for the auto lock delay. //Buzzer BT_MESH_MODEL_PUB_DEFINE(buzzer_srv_pub, NULL, 1); //1 Byte for the On|Off|Alarms //Light BT_MESH_MODEL_PUB_DEFINE(light_srv_pub, NULL, 1); //1 Byte for the On|Off|Alarms //Battery type enum battery_type_t { BATTERY_TYPE_UNKNOWN = 0, BATTERY_TYPE_LEAN_ACID = 1, BATTERY_TYPE_LITHIUM = 2, BATTERY_TYPE_DIRECT_POWER = 3, BATTERY_TYPE_DRY = 4, }; //Lock Type enum lock_onoff_t { LOCK_OFF = 0, LOCK_ON = 1, }; //Buzzer and Light Type enum onoffalarm_t { OFF = 0, ON = 1, ALARMS_ONLY = 2, }; //Structure to keep the battery data typedef struct battery_data_t { uint16_t voltage_mv; enum battery_type_t type; }battery_data_t; //Lock Structure typedef struct lock_data_t { enum lock_onoff_t status; uint16_t lock_delay; }lock_data_t; //Buzzer typedef struct buzzer_data_t { enum onoffalarm_t status; }buzzer_data_t; //Light typedef struct light_data_t { enum onoffalarm_t status; }light_data_t; //Battery data Initial //TODO: Implement a function that saves the battery data in the flash memory, so it can be restored after a reboot. static battery_data_t battery_data = { .voltage_mv = 0, .type = BATTERY_TYPE_UNKNOWN, }; //Auto lock data initial static lock_data_t lock_data = { .status = LOCK_OFF, .lock_delay = 10, }; //Auto Buzzer data initial static buzzer_data_t buzzer_data = { .status = OFF, }; //Auto Light data initial static light_data_t light_data = { .status = OFF, }; //Functions prototypes, to be used in the custom model //Handlers - Functions that will be called when a message is received static void battery_get(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf); static void battery_set_unack(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf); static void battery_status(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf); static void lock_onoff_get(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctn, struct net_buf_simple *buf); static void lock_onoff_set_unack(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctn, struct net_buf_simple *buf); static void lock_onoff_status(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctn, struct net_buf_simple *buf); static void buzzer_onoff_get(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctn, struct net_buf_simple *buf); static void buzzer_onoff_set_unack(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctn, struct net_buf_simple *buf); static void buzzer_onoff_status(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctn, struct net_buf_simple *buf); static void light_onoff_get(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctn, struct net_buf_simple *buf); static void light_onoff_set_unack(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctn, struct net_buf_simple *buf); static void light_onoff_status(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctn, struct net_buf_simple *buf); //Model Opcodes //Battery static const struct bt_mesh_model_op battery_op_list[]; //Lock static const struct bt_mesh_model_op lock_op_list[]; //Buzzer static const struct bt_mesh_model_op buzzer_op_list[]; //Light static const struct bt_mesh_model_op light_op_list[]; /** * Device server model handlers Functions ================================================================================================= */ /* Battery Handlers Functions */ static void battery_get(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf) { LOG_INF("Battery Get message received\n"); //LOGGING SOME INFO LOG_INF("ctx->addr: 0x%04x\n", ctx->addr); LOG_INF("ctx->net_idx: 0x%04x\n", ctx->net_idx); LOG_INF("ctx->app_idx: 0x%04x\n", ctx->app_idx); LOG_INF("ctx->recv_dst: 0x%04x\n", ctx->recv_dst); //TODO: Implement a function that reads the battery voltage from serial interface //For now, we will use a random value battery_data.voltage_mv = 1000 + (rand() % 1000); LOG_INF("Battery voltage: %d\n", battery_data.voltage_mv); LOG_INF("Battery type: %d\n", battery_data.type); //Send the battery voltage publish_battery_status(model, ctx, battery_data.voltage_mv, battery_data.type); } static void battery_set_unack(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf) { LOG_INF("Battery Set Unack message received\n"); //Set the battery type battery_data.type = net_buf_simple_pull_u8(buf); LOG_INF("Battery type set to: %d\n", battery_data.type); } static void battery_status(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf) { LOG_INF("Replying with STATUS message\n"); //Send the battery status } /* END - Battery Functions Handlers */ /* Lock Functions Handlers */ static void lock_onoff_get(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf) { LOG_INF("Lock Get message received\n"); //TODO: Implement the function that set on off in the lock LOG_INF("Lock Status: %d | Lock Delay: %d", lock_data.status, lock_data.lock_delay); //Send the lock status publish_lock_status(model, ctx, lock_data.status, lock_data.lock_delay); } static void lock_onoff_set_unack(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf) { LOG_INF("Lock SET message received!"); uint16_t delay = net_buf_simple_pull_le16(buf); /*delay*/ uint8_t status = net_buf_simple_pull_u8(buf); /*OnOFF*/ if(status) { LOG_INF("LOCK ON, with delay of %d", delay); lock_data.status = LOCK_ON; lock_data.lock_delay = delay; } else { LOG_INF("LOCK OFF, with delay of %d", delay); lock_data.status = LOCK_OFF; lock_data.lock_delay = delay; } } static void lock_onoff_status(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf) { LOG_INF("Lock STATUS message received!"); } /* END - Lock Functions Handlers */ /* Buzzer Functions Handlers */ static void buzzer_onoff_get(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf) { LOG_INF("Buzzer GET Message Received"); LOG_INF("Buzzer Status: %d", buzzer_data.status); //Send buzzer data publish_buzzer_status(model, ctx, buzzer_data.status); } static void buzzer_onoff_set_unack(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf) { LOG_INF("Buzzer SET Message Received"); uint8_t status = net_buf_simple_pull_u8(buf); if(status == 0) { LOG_INF("Buzzer OFF"); buzzer_data.status = OFF; } else if(status == 1) { LOG_INF("Buzzer ON"); buzzer_data.status = ON; } else if(status == 2) { LOG_INF("Buzzer ALARMS ONLY"); buzzer_data.status = ALARMS_ONLY; } else { LOG_INF("Invalid status"); } } static void buzzer_onoff_status(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf) { LOG_INF("Buzzer STATUS message received!"); } /* END - Buzzer Functions Handlers */ /* Light Functions Handlers */ static void light_onoff_get(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf) { LOG_INF("Light GET Message Received"); LOG_INF("Light Status: %d", light_data.status); //Send light data publish_light_status(model, ctx, light_data.status); } static void light_onoff_set_unack(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf) { LOG_INF("Light SET Message Received"); uint8_t status = net_buf_simple_pull_u8(buf); if(status == 0) { LOG_INF("Light OFF"); light_data.status = OFF; } else if(status == 1) { LOG_INF("Light ON"); light_data.status = ON; } else if(status == 2) { LOG_INF("Light ALARMS ONLY"); light_data.status = ALARMS_ONLY; } else { LOG_INF("Invalid status"); } } static void light_onoff_status(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf) { LOG_INF("Light STATUS message received!"); } /* END - Light Functions Handlers */ /** * Device server model Publication ================================================================================================= */ //Publish the battery voltage and type void publish_battery_status(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, uint16_t voltage_mv, enum battery_type_t type) { int err; //Check if the model has a address assigned if(model->pub->addr == BT_MESH_ADDR_UNASSIGNED) { LOG_INF("No publish address associated with the model\n"); return; } struct net_buf_simple *msg = model->pub->msg; bt_mesh_model_msg_init(msg, BATTERY_STATUS_OP); net_buf_simple_add_le16(msg, voltage_mv); net_buf_simple_add_u8(msg, type); LOG_INF("Publishing battery status\n"); //Publish the message err = bt_mesh_model_publish(model); if(err) { LOG_INF("bt_mesh_model_publish err: %d\n", err); } } //Publish the lock status void publish_lock_status(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, enum lock_onoff_t status, uint16_t delay) { int err; if(model->pub->addr == BT_MESH_ADDR_UNASSIGNED) { LOG_INF("No publish address associated with the model\n"); return; } struct net_buf_simple *msg = model->pub->msg; bt_mesh_model_msg_init(msg, LOCK_STATUS_OP); net_buf_simple_add_le16(msg, delay); net_buf_simple_add_u8(msg, status); LOG_INF("Publishing LOCK status\n"); //Publish the message err = bt_mesh_model_publish(model); if(err) { LOG_INF("bt_mesh_model_publish err: %d\n", err); } } //Publish the buzzer status void publish_buzzer_status(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, enum onoffalarm_t status) { int err; if(model->pub->addr == BT_MESH_ADDR_UNASSIGNED) { LOG_INF("No publish address associated with the model\n"); return; } struct net_buf_simple *msg = model->pub->msg; bt_mesh_model_msg_init(msg, BUZZER_STATUS_OP); net_buf_simple_add_u8(msg, status); LOG_INF("Publishing BUZZER status\n"); //Publish the message err = bt_mesh_model_publish(model); if(err) { LOG_INF("bt_mesh_model_publish err: %d\n", err); } } //Publish the light status void publish_light_status(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, enum onoffalarm_t status) { int err; if(model->pub->addr == BT_MESH_ADDR_UNASSIGNED) { LOG_INF("No publish address associated with the model\n"); return; } struct net_buf_simple *msg = model->pub->msg; bt_mesh_model_msg_init(msg, LIGHT_STATUS_OP); net_buf_simple_add_u8(msg, status); LOG_INF("Publishing LIGHT status\n"); //Publish the message err = bt_mesh_model_publish(model); if(err) { LOG_INF("bt_mesh_model_publish err: %d\n", err); } } /** * Server Model Entries ================================================================================================= */ static const struct bt_mesh_model_op battery_op_list[] = { {BATTERY_GET_OP, BT_MESH_LEN_MIN(0), battery_get}, {BATTERY_TYPE_SET_UNACK_OP, BT_MESH_LEN_MIN(1), battery_set_unack}, {BATTERY_STATUS_OP, BT_MESH_LEN_MIN(1), battery_status}, BT_MESH_MODEL_OP_END, }; static const struct bt_mesh_model_op lock_op_list[] = { {LOCK_GET_OP, BT_MESH_LEN_MIN(0), lock_onoff_get}, {LOCK_SET_UNACK_OP, BT_MESH_LEN_MIN(0), lock_onoff_set_unack}, {LOCK_STATUS_OP, BT_MESH_LEN_MIN(0), lock_onoff_status}, BT_MESH_MODEL_OP_END, }; static const struct bt_mesh_model_op buzzer_op_list[] = { {BUZZER_GET_OP, BT_MESH_LEN_MIN(0), buzzer_onoff_get}, {BUZZER_SET_UNACK_OP, BT_MESH_LEN_MIN(0), buzzer_onoff_set_unack}, {BUZZER_STATUS_OP, BT_MESH_LEN_MIN(0), buzzer_onoff_status}, BT_MESH_MODEL_OP_END, }; static const struct bt_mesh_model_op light_op_list[] = { {LIGHT_GET_OP, BT_MESH_LEN_MIN(0), light_onoff_get}, {LIGHT_SET_UNACK_OP, BT_MESH_LEN_MIN(0), light_onoff_set_unack}, {LIGHT_STATUS_OP, BT_MESH_LEN_MIN(0), light_onoff_status}, BT_MESH_MODEL_OP_END, }; /** * Health Server ================================================================================================= */ static bool attention; static void attention_on(struct bt_mesh_model *mod) { attention = true; LOG_INF("attention_on\n"); } static void attention_off(struct bt_mesh_model *mod) { attention = false; LOG_INF("attention_off\n"); } static const struct bt_mesh_health_srv_cb health_srv_cb = { .attn_on = attention_on, .attn_off = attention_off, }; static struct bt_mesh_health_srv health_srv = { .cb = &health_srv_cb, }; BT_MESH_HEALTH_PUB_DEFINE(health_pub, 0); struct bt_mesh_model root_models[] = { BT_MESH_MODEL_CFG_SRV, BT_MESH_MODEL_HEALTH_SRV(&health_srv, &health_pub), BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_SRV, lock_op_list, &lock_srv_pub, &lock_data), }; /** * Device server Model ================================================================================================= */ struct bt_mesh_model s0_models[] = { BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_SRV, buzzer_op_list, &buzzer_srv_pub, &buzzer_data), }; struct bt_mesh_model s1_models[] = { BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_SRV, light_op_list, &light_srv_pub, &light_data), }; struct bt_mesh_model parking_device_server_model [] = { BT_MESH_MODEL_VND(CONFIG_BT_COMPANY_ID, MY_BATTERY_MODEL_ID, battery_op_list, &battery_srv_pub, &battery_data), }; static struct bt_mesh_elem elements[] = { BT_MESH_ELEM(0, root_models, parking_device_server_model), BT_MESH_ELEM(1, s0_models, BT_MESH_MODEL_NONE), BT_MESH_ELEM(2, s1_models, BT_MESH_MODEL_NONE), }; static const struct bt_mesh_comp comp = { .cid = CONFIG_BT_COMPANY_ID, .elem = elements, .elem_count = ARRAY_SIZE(elements), }; const struct bt_mesh_comp *model_handler_init(void) { if(IS_ENABLED(CONFIG_BT_SETTINGS)) { settings_subsys_init(); settings_load(); } return ∁ }
#General settings CONFIG_MAIN_STACK_SIZE=4096 CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096 CONFIG_FLASH=y CONFIG_FLASH_PAGE_LAYOUT=y CONFIG_FLASH_MAP=y CONFIG_FCB=y CONFIG_SETTINGS=y CONFIG_SETTINGS_FCB=y CONFIG_HWINFO=y CONFIG_DK_LIBRARY=y #Bluetooth settings CONFIG_BT=y #Company ID used by the Bluetooth SIG, for testing purposes we will use nordic semi ID. CONFIG_BT_COMPANY_ID=0X0059 CONFIG_BT_OBSERVER=y CONFIG_BT_SETTINGS=y CONFIG_BT_PERIPHERAL=y CONFIG_BT_DEVICE_NAME="Parking Device" #Mesh settings CONFIG_BT_MESH=y CONFIG_BT_MESH_APP_KEY_COUNT=1 CONFIG_BT_MESH_RELAY=y CONFIG_BT_MESH_FRIEND=y CONFIG_BT_MESH_GATT_PROXY=y CONFIG_BT_MESH_PROXY_USE_DEVICE_NAME=y CONFIG_BT_MESH_BEACON_ENABLED=n CONFIG_BT_MESH_GATT_PROXY=y CONFIG_BT_MESH_PB_GATT=y CONFIG_BT_MESH_PB_ADV=y CONFIG_BT_MESH_CRPL=32 CONFIG_BT_MESH_MSG_CACHE_SIZE=64 CONFIG_BT_MESH_SHELL=y CONFIG_BT_MESH_DK_PROV=y #Mesh models settings #Logging settings CONFIG_LOG=y CONFIG_BT_MESH_LOG_LEVEL_DBG=y CONFIG_BT_MESH_LOG_LEVEL_INF=y #J-Link LOG via RTT CONFIG_UART_CONSOLE=n CONFIG_USE_SEGGER_RTT=y CONFIG_RTT_CONSOLE=y CONFIG_LOG_BACKEND_RTT=y