Bluetooth Mesh - Vendor specific OpCodes/ModelIds

Hello,

I am currently working with a couple of nRF52833 development kits.

My software is based on the bluetooth mesh light & light switch examples and extends them by adding additional client as well as server models.

To achieve this, I added vendor model IDs as well as vendor OpCodes. I kept the company ID set to nordic.

The model is initialized as seen below:

#define BT_MESH_VENDOR_MODEL_HEARTBEAT_CLI(_cli)                               				\
	BT_MESH_MODEL_VND_CB(VENDOR_COMPANY_ID,													\
						BT_MESH_MODEL_ID_VENDOR_HEARTBEAT_CLI,                   			\
			 			_vendor_heartbeat_cli_opcode_list, &(_cli)->pub,                  	\
			 			BT_MESH_MODEL_USER_DATA(struct bt_mesh_vendor_heartbeat_cli, _cli),	\
			 			&_bt_mesh_vendor_heartbeat_cli_cb)

My issue arises, whenever I use the BT_MESH_MODEL_OP_3 macro to setup the vendor OpCodes. Doing so will throw the following error:

  • <err> bt_mesh_access: No OpCode 0x00d30059 for elem 0x159
  • The Macro I use looks like this
    • #define BT_MESH_VENDOR_HEARTBEAT_STATUS_OP BT_MESH_MODEL_OP_3(0x13, VENDOR_COMPANY_ID)
  • With VENDOR_COMPANY_ID set to 0x0059 (Nordic)
  • The Model ID for client/server is the following:
    • #define BT_MESH_MODEL_ID_VENDOR_HEARTBEAT_CLI 0x000F
      #define BT_MESH_MODEL_ID_VENDOR_HEARTBEAT_SRV 0x0010

Based on my understanding, the above setup should be correct.

My code works fine if I swap the vendor models/opcodes for some generic one like from the OnOff Client/Server.

Looking forward to your feedback. Maybe I'm just overlooking some obvious mistake in my setup.

Thank you.

Best regards,

Andreas

  • Hi,

    It is not quite clear to me exactly what you have done, since a lot of the code is omitted or rephrased. The excerpts that you have shared looks fine from what I can tell, though.

    In any case, the log message indicates that the device receives a message to an element, and there are no models using that opcode on that element. In other words, some model on some device is sending a message, using the 3 byte opcode 0xd30059, and the device providing the log error message receives this message to an element without a model handling that opcode. The error may be either in the sending model (using the wrong opcode) or in the receiving model (using the wrong opcode) or in configuration (a model is configured to address a message to an element without support for receiving the given opcode.)

    How to create vendor models is thoroughly documented. If you have any reason to believe something may be wrong with your vendor model implementation, then I highly recommend a second look at the following documentation:

    Regards,
    Terje

  • Hi Terje,

    Thank you for your feedback.

    As I mentioned, the part that makes me wonder is that I only need to redefine the opcodes to some generic ones (On/Off in this case), to make the whole communication work as expected.


    Please see the code snippet below:

    // Model message Opcodes
    #define BT_MESH_VENDOR_HEARTBEAT_GET_OP BT_MESH_MODEL_OP_3(0x12, VENDOR_COMPANY_ID)
    #define BT_MESH_VENDOR_HEARTBEAT_STATUS_OP BT_MESH_MODEL_OP_3(0x13, VENDOR_COMPANY_ID)
    //#define BT_MESH_VENDOR_HEARTBEAT_GET_OP BT_MESH_MODEL_OP_2(0x82, 0x01)
    //#define BT_MESH_VENDOR_HEARTBEAT_STATUS_OP BT_MESH_MODEL_OP_2(0x82, 0x04)

    The server and client models access the same .h file to access the OpCodes, so there is no configuration mismatch as it works when switching to the generic ones.

    I followed the "Vendor model development overview" as guideline during the initial software setup.
    I also checked the chat example for any differences that would explain my issues, but it looks fine to me.


    You mentioned, that I did not provide much of my code. I am happy to share more if needed.


    Best regards,
    Andreas

  • Hi,

    Did you get any further?

    It could be for instance a (buffer) size issue (since vendor specific opcodes are three bytes, not one or two.)

    It could also be that you are missing (or have not properly registered) the opcode for the receiving model, and when you try with a SIG defined opcode you happen to use an opcode which is already handled by an existing model, so that model gets the message (and therefore you do not get the error message.)

    Regards,
    Terje

  • Hi,

    Not really no.

    At the moment I am using different SIG defined opcodes for each of my vendor models which allows me to have them running without any error messages.

    But of course, thats only ok for the time being.

    Below, please find the Client and Server part of one of the models. I also included the SIG opcodes I use at the moment as well as the vendor codes which are commented out.

    #include "../include/vendor_hit_cli.h"
    #include <stdlib.h>
    #include <dk_buttons_and_leds.h>
    #include "../../../../../ncs/v2.5.0/nrf/subsys/bluetooth/mesh/model_utils.h"
    
    
    /* Decode the data that is sent with the message acknowledgement from the server.*/
    static int vendor_hit_decode_status(struct net_buf_simple *buf,
    			  struct bt_mesh_vendor_playerstatus_status *status)
    {
    	//printk("Hit_Client: vendor_hit_decode_status\n");
    
    	status->player_id = net_buf_simple_pull_u8(buf);
    	printk("\tplayer_id: %d\n", status->player_id);
    	status->player_lifecount = net_buf_simple_pull_u8(buf);
    	printk("\tplayer_lifecount: %d\n", status->player_lifecount);
    
        return 0;
    }
    
    /* Callback that handles the status response from the server.*/
    static int vendor_hit_status_cb(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
    			 struct net_buf_simple *buf)
    {
    	//printk("Hit_Client: vendor_hit_status_cb\n");
    	printk("Hit_Client: Status Message received\n");
    	if (buf->len > BT_MESH_VENDOR_HIT_MSG_STATUS_MAX_LEN ) {
    		printk("buf->len too long: %d\n", buf->len);
    		//return -EMSGSIZE;
    	}
    
    	struct bt_mesh_vendor_hit_cli *cli = model->user_data;
    	struct bt_mesh_vendor_playerstatus_status status;
    	struct bt_mesh_vendor_playerstatus_status *rsp;
    	int err;
    
    	err = vendor_hit_decode_status(buf, &status);
    	if (err) {
    		return err;
    	} else {
    		//printk("Hit_Client: vendor_hit_status_cb - SUCCESS\n");
    	}
    
    	if (bt_mesh_msg_ack_ctx_match(&cli->ack_ctx, BT_MESH_VENDOR_HIT_STATUS_OP, ctx->addr,
    				      (void **)&rsp)) {
    		*rsp = status;
    		bt_mesh_msg_ack_ctx_rx(&cli->ack_ctx);
    		//printk("Hit_Client: vendor_hit_status_cb - Marked Acknowledged\n");
    	}
    
    	if (cli->handlers->status) {
    		cli->handlers->status(cli, ctx, &status);
    	}
    
    	return 0;
    }
    
    /* Define opcodes */
    const struct bt_mesh_model_op _vendor_hit_cli_opcode_list[] = {
    	{
    		BT_MESH_VENDOR_HIT_STATUS_OP,
    		BT_MESH_LEN_MIN(BT_MESH_VENDOR_HIT_MSG_STATUS_MIN_LEN),
    		vendor_hit_status_cb,
    	},
    	BT_MESH_MODEL_OP_END,
    };
    
    /* Reset model */
    static void bt_mesh_vendor_hit_cli_reset(struct bt_mesh_model *model)
    {
    	printk("Hit_Client: bt_mesh_vendor_hit_cli_reset\n");
    	struct bt_mesh_vendor_hit_cli *cli = model->user_data;
    
    	net_buf_simple_reset(cli->pub.msg);
    	bt_mesh_msg_ack_ctx_reset(&cli->ack_ctx);
    }
    
    /* Initialize model */
    static int bt_mesh_vendor_hit_cli_init(struct bt_mesh_model *model)
    {
    	printk("Hit_Client: bt_mesh_vendor_hit_cli_init\n");
    	struct bt_mesh_vendor_hit_cli *cli = model->user_data;
    
    	cli->model = model;
    	cli->pub.msg = &cli->pub_buf;
    	net_buf_simple_init_with_data(&cli->pub_buf, cli->pub_data,
    				      sizeof(cli->pub_data));
    	bt_mesh_msg_ack_ctx_init(&cli->ack_ctx);
    
    	return 0;
    }
    
    /* Callbacks for the hit client model */
    const struct bt_mesh_model_cb _bt_mesh_vendor_hit_cli_cb = {
    	.init = bt_mesh_vendor_hit_cli_init,
    	.reset = bt_mesh_vendor_hit_cli_reset,
    };
    
    /* Send set message to the server model. */
    int bt_mesh_vendor_hit_cli_set(struct bt_mesh_vendor_hit_cli *cli,
    			  struct bt_mesh_msg_ctx *ctx,
    			  const struct bt_mesh_vendor_hit_set *set,
    			  struct bt_mesh_vendor_playerstatus_status *rsp)
    {
    	printk("Hit_Client: Communicate Hit... Player ID: %d Player Lifecount: %d\n", set->player_id, set->player_lifecount);
        if (!set->reuse_transaction) {
    		cli->tid++;
    	}
    
    	BT_MESH_MODEL_BUF_DEFINE(msg, BT_MESH_VENDOR_HIT_SET_OP,
    				 BT_MESH_VENDOR_HIT_MSG_SET_MAX_LEN);
    	bt_mesh_model_msg_init(&msg, BT_MESH_VENDOR_HIT_SET_OP);
    	//printk("Message OP: %02X\n", BT_MESH_VENDOR_HIT_SET_OP);
    	
     	net_buf_simple_add_u8(&msg, set->player_id);
    	net_buf_simple_add_u8(&msg, set->player_lifecount);
    	net_buf_simple_add_u8(&msg, cli->tid);
    
    
    	struct bt_mesh_msg_rsp_ctx rsp_ctx = {
    		.ack = &cli->ack_ctx,
    		.op = BT_MESH_VENDOR_HIT_STATUS_OP,
    		.user_data = rsp,
    		.timeout = model_ackd_timeout_get(cli->model, ctx),
    	};
    	//printk("Response payload: %d\n", rsp);
    	//printk("Expected response OP: %02X\n", BT_MESH_VENDOR_HIT_STATUS_OP);
    
    	return bt_mesh_msg_ackd_send(cli->model, ctx, &msg, rsp ? &rsp_ctx : NULL);
    }


    /*
    * Vendor client model that implements a hit model.
    * Message from the client is sent after the client (hit detection) recognizes a hit. This is triggered outside the bluetooth environment.
    * The model then sends an acknowledged SET message to the server (watergun) to update the lifes remaining.
    * The message payload consists of the playerid (hit detection and watergun belong to the same player) and the remaining lifes. Both are bit encoded.
    */
    
    #ifndef VENDOR_HIT_CLI_H
    #define VENDOR_HIT_CLI_H
    
    #include "vendor_model_common.h"
    
    struct bt_mesh_vendor_hit_cli;
    
    /** @def BT_MESH_VENDOR_HIT_CLI_INIT
     *
     * @brief Initialization parameters for a @ref bt_mesh_vendor_hit_cli instance.
     *
     * @param[in] _status_handler Optional status message handler.
     */
    #define BT_MESH_VENDOR_HIT_CLI_INIT(_handlers)	\
    	{                                                   \
    		.handlers = _handlers,                          \
    	}
    
    /** @def BT_MESH_MODEL_VENDOR_HIT_CLI
     *
     * @brief Vendor HIT Client model composition data entry.
     *
     * @param[in] _cli Pointer to a @ref bt_mesh_vendor_hit_cli instance.
     */
    #define BT_MESH_VENDOR_MODEL_HIT_CLI(_cli)                               				\
    	BT_MESH_MODEL_VND_CB(VENDOR_COMPANY_ID,													\
    						BT_MESH_MODEL_ID_VENDOR_HIT_CLI,                   			\
    			 			_vendor_hit_cli_opcode_list, &(_cli)->pub,                  	\
    			 			BT_MESH_MODEL_USER_DATA(struct bt_mesh_vendor_hit_cli, _cli),	\
    			 			&_bt_mesh_vendor_hit_cli_cb)
    
    
    /** Generic Vendor Hit Client state access handlers. */
    struct bt_mesh_vendor_hit_cli_handlers {
    	/** @brief Get the Hit state.
    	 *
    	 * @note This handler is mandatory.
    	 *
    	 * @param[in] cli Client instance to get the state of.
    	 * @param[in] ctx Message context for the message that triggered the
    	 * change, or NULL if the change is not coming from a message.
    	 * @param[out] rsp Response structure to be filled.
    	 */
    	void (*const status)(struct bt_mesh_vendor_hit_cli *cli,
    			  struct bt_mesh_msg_ctx *ctx,
    			  struct bt_mesh_vendor_playerstatus_status *rsp);
    };
                                             
    /**
     * Vendor Hit Client structure.
     *
     * Should be initialized with the @ref BT_MESH_VENDOR_HIT_CLI_INIT macro.
     */
    struct bt_mesh_vendor_hit_cli {
    	/** Current Transaction ID. */
    	uint8_t tid;
    	/** Response context for tracking acknowledged messages. */
    	struct bt_mesh_msg_ack_ctx ack_ctx;
    	/** Publish parameters. */
    	struct bt_mesh_model_pub pub;
    	/* Publication buffer */
    	struct net_buf_simple pub_buf;
    	/* Publication data */
    	uint8_t pub_data[BT_MESH_MODEL_BUF_LEN(BT_MESH_VENDOR_HIT_SET_OP,
    					       BT_MESH_VENDOR_HIT_MSG_SET_MAX_LEN)];
    	/** Access model pointer. */
    	struct bt_mesh_model *model;
    	/** Handler function structure. */
    	const struct bt_mesh_vendor_hit_cli_handlers *handlers;
    };
    
    /** @brief Set the Hit state in the srv.
     *
     * This call is blocking if the @p rsp buffer is non-NULL. Otherwise, this
     * function will return, and the response will be passed to the
     * @ref bt_mesh_vendor_heartbeat_cli::status_handler callback.
     *
     * @param[in] cli Client model to send on.
     * @param[in] ctx Message context, or NULL to use the configured publish
     * parameters.
     * @param[in] set New hit parameters to set. @p set::transition can either
     * point to a transition structure, or be left to NULL to use the default
     * transition parameters on the server.
     * @param[out] rsp Status response buffer, or NULL to keep from blocking.
     *
     * @retval 0 Successfully sent the message and populated the @p rsp buffer.
     * @retval -EALREADY A blocking request is already in progress.
     * @retval -EADDRNOTAVAIL A message context was not provided and publishing is
     * not configured.
     * @retval -EAGAIN The device has not been provisioned.
     * @retval -ETIMEDOUT The request timed out without a response.
     */
    int bt_mesh_vendor_hit_cli_set(struct bt_mesh_vendor_hit_cli *cli,
    			  struct bt_mesh_msg_ctx *ctx,
    			  const struct bt_mesh_vendor_hit_set *set,
    			  struct bt_mesh_vendor_playerstatus_status *rsp);
    
    /** @cond INTERNAL_HIDDEN */
    extern const struct bt_mesh_model_op _vendor_hit_cli_opcode_list[];
    extern const struct bt_mesh_model_cb _bt_mesh_vendor_hit_cli_cb;
    /** @endcond */
    
    
    #endif

    Above code is the "Hit-Client"

    #include "../include/vendor_hit_srv.h"
    #include <stdlib.h>
    #include <dk_buttons_and_leds.h>
    #include <zephyr/bluetooth/mesh/msg.h>
    #include "../../../../ncs/v2.5.0/nrf/subsys/bluetooth/mesh/model_utils.h"
    
    /* Encode the data that is sent with the message acknowledgement to the client. */
    static void vendor_hit_encode_status(struct net_buf_simple *buf,
    			  const struct bt_mesh_vendor_playerstatus_status *status)
    {
    	//printk("Hit_Server: vendor_hit_encode_status\n");
    	bt_mesh_model_msg_init(buf, BT_MESH_VENDOR_HIT_STATUS_OP);
    
    	//Some debugging
    	// TODO: Remove this
    	printk("Hit_Server: Sending status message..\n\tplayer_id: %d, Lifecount: %d\n", status->player_id, status->player_lifecount);
    
        net_buf_simple_add_u8(buf, status->player_id);
        net_buf_simple_add_u8(buf, status->player_lifecount);
    }
    
    /* Handle the response status message to the client. */
    static void vendor_hit_rsp_status(struct bt_mesh_model *model,
    		       struct bt_mesh_msg_ctx *rx_ctx,
    		       const struct bt_mesh_vendor_playerstatus_status *status)
    {
    	//printk("Hit_Server: vendor_hit_rsp_status\n");
    	BT_MESH_MODEL_BUF_DEFINE(reply, BT_MESH_VENDOR_HIT_STATUS_OP, BT_MESH_VENDOR_HIT_MSG_STATUS_MAX_LEN);
    	vendor_hit_encode_status(&reply, status);
    
    	(void) bt_mesh_model_send(model, rx_ctx, &reply, NULL, NULL);
    }
    
    /* Handles the get request from the client. */
    static int vendor_hit_handle_get(struct bt_mesh_model *model,
                                   struct bt_mesh_msg_ctx *ctx,
                                   struct net_buf_simple *buf)
    {
    	//printk("Hit_Server: vendor_hit_handle_get\n");
        //struct bt_mesh_vendor_test_model_srv *srv = model->user_data;
    	
    	//TODO Uncommenting below line will cause the programm to crash with a hard fault.
    	//srv->handlers->get(srv, ctx, &player_status);
    
    	vendor_hit_rsp_status(model, ctx, &local_player_status);
    
    	return 0;
    }
    
    /* Get the data from the set message and transfer it to the application. */
    static int vendor_hit_update_lifecount_self(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
    		      struct net_buf_simple *buf, bool ack)
    {
    	//printk("Hit_Server: vendor_hit_update_lifecount_self\n");
    	if (buf->len != BT_MESH_VENDOR_HIT_MSG_SET_MIN_LEN &&
    	    buf->len != BT_MESH_VENDOR_HIT_MSG_SET_MAX_LEN) {
    		printk("buf->len too long: %d\n", buf->len);
    		//return -EMSGSIZE;
    	}
    
    	struct bt_mesh_vendor_hit_srv *srv = model->user_data;
    	struct bt_mesh_vendor_hit_set set;
    
    	uint8_t player_id = net_buf_simple_pull_u8(buf);	
    	uint8_t player_lifecount = net_buf_simple_pull_u8(buf);	
    	uint8_t tid = net_buf_simple_pull_u8(buf);
    
    	//printk("Player_id: %d ", player_id);	
    	//printk("Lifecount: %d\n", player_lifecount);	
    	//printk("tid: %d\n", tid);
    
    	set.player_lifecount = player_lifecount;
    	set.player_id = player_id;
    
    	if (tid_check_and_update(&srv->prev_transaction, tid, ctx) != 0) {
    		// If this is the same transaction, we don't need to send it
    		// to the app, but we still have to respond with a status.
    		//
    		srv->handlers->get(srv, NULL, &local_player_status);
    		goto respond;
    	}
        
    	srv->handlers->set(srv, ctx, &set, &local_player_status);
    	// TODO Check if below code is necessary.
    	//(void)vendor_hit_srv_pub(srv, NULL, &local_player_status);
    	respond:
    		if (ack) {
    			vendor_hit_rsp_status(model, ctx, &local_player_status);
    		}
    
    	return 0;
    }
    /* Handle the set message from the client*/
    static int vendor_hit_handle_set(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
    		      struct net_buf_simple *buf)
    {
    	//printk("Hit_Server: vendor_hit_handle_set\n");
    	return vendor_hit_update_lifecount_self(model, ctx, buf, true);
    }
    
    /* Reset the model */
    static void vendor_hit_srv_reset(struct bt_mesh_model *model)
    {
    	printk("Hit_Server: vendor_hit_srv_reset\n");
    	net_buf_simple_reset(model->pub->msg);
    }
    
    /* Initialize the model */
    static int vendor_hit_srv_init(struct bt_mesh_model *model)
    {
    	printk("Hit_Server: vendor_hit_srv_init\n");
    	//int err;
    	struct bt_mesh_vendor_hit_srv *srv = model->user_data;
    	
    	srv->model = model;
    	srv->pub.msg = &srv->pub_buf;
    	net_buf_simple_init_with_data(&srv->pub_buf, srv->pub_data,
    				      sizeof(srv->pub_data));
    	return 0;
    }
    
    /* Model callbacks */
    const struct bt_mesh_model_cb _bt_mesh_vendor_hit_srv_cb = {
    	.init = vendor_hit_srv_init,
    	.reset = vendor_hit_srv_reset,
    };
    
    /* Publish status message */
    int vendor_hit_srv_pub(struct bt_mesh_vendor_hit_srv *srv,
    			  struct bt_mesh_msg_ctx *ctx,
    			  const struct bt_mesh_vendor_playerstatus_status *status)
    {
    	//printk("Hit_Server: vendor_hit_srv_pub\n");
    	BT_MESH_MODEL_BUF_DEFINE(msg, BT_MESH_VENDOR_HIT_STATUS_OP,
    				 BT_MESH_VENDOR_HIT_MSG_STATUS_MAX_LEN);
    	vendor_hit_encode_status(&msg, status);
    	return bt_mesh_msg_send(srv->model, ctx, &msg);
    }
    
    /* Opcode list */
    const struct bt_mesh_model_op _vendor_hit_srv_opcode_list[] = {
    	{ BT_MESH_VENDOR_HIT_GET_OP, BT_MESH_VENDOR_HIT_MSG_GET_MIN_LEN, vendor_hit_handle_get },
    	{ BT_MESH_VENDOR_HIT_SET_OP, BT_MESH_VENDOR_HIT_MSG_SET_MIN_LEN, vendor_hit_handle_set },
        BT_MESH_MODEL_OP_END,
    };

    /*
    * Vendor server model that implements a hit model.
    * Message from the client is sent after the client (hit detection) recognizes a hit. This is triggered outside the bluetooth environment.
    * The model then sends an acknowledged SET message to the server (watergun) to update the lifes remaining.
    * The message payload consists of the playerid (hit detection and watergun belong to the same player) and the remaining lifes. Both are bit encoded.
    */
    
    #ifndef VENDOR_HIT_SRV_H
    #define VENDOR_HIT_SRV_H
    
    #include "vendor_model_common.h"
    
    struct bt_mesh_vendor_hit_srv;
    
    /** @def BT_MESH_VENDOR_HIT_SRV_INIT
     *
     * @brief Init parameters for a @ref bt_mesh_vendor_hit_srv instance.
     *
     * @param[in] _handlers State access handlers to use in the model instance.
     */
    #define BT_MESH_VENDOR_HIT_SRV_INIT(_handlers)              \
    	{                                                             \
    		.handlers = _handlers,                                    \
    	}
    
    
    /** @def BT_MESH_VENDOR_MODEL_HIT_SRV
     *
     * @brief Vendor Hit model composition data entry.
     *
     * @param[in] _srv Pointer to a @ref bt_mesh_vendor_hit_srv instance.
     */
    #define BT_MESH_VENDOR_MODEL_HIT_SRV(_srv)                                            	\
    	BT_MESH_MODEL_VND_CB(VENDOR_COMPANY_ID,														\
    						BT_MESH_MODEL_ID_VENDOR_HIT_SRV,                               	\
    			 			_vendor_hit_srv_opcode_list, &(_srv)->pub,                      	\
    			 			BT_MESH_MODEL_USER_DATA(struct bt_mesh_vendor_hit_srv, _srv),  	\
    			 			&_bt_mesh_vendor_hit_srv_cb)
    
    int vendor_hit_srv_pub(struct bt_mesh_vendor_hit_srv *srv,
    			  struct bt_mesh_msg_ctx *ctx,
    			  const struct bt_mesh_vendor_playerstatus_status *status);
    
    /** Generic Vendor Hit Server state access handlers. */
    struct bt_mesh_vendor_hit_srv_handlers {
    	/** @brief Set the Hit state.
    	 *
    	 * When a set message is received, the model publishes a status message, with the response
    	 * set to @c rsp. When an acknowledged set message is received, the model also sends a
    	 * response back to a client.
    	 *
    	 * @note This handler is mandatory.
    	 *
    	 * @param[in] srv Server instance to set the state of.
    	 * @param[in] ctx Message context for the message that triggered the
    	 * change, or NULL if the change is not coming from a message.
    	 * @param[in] set Parameters of the state change.
    	 * @param[out] rsp Response structure to be filled.
    	 */
    	void (*const set)(struct bt_mesh_vendor_hit_srv *srv,
    			  struct bt_mesh_msg_ctx *ctx,
    			  const struct bt_mesh_vendor_hit_set *set,
    			  struct bt_mesh_vendor_playerstatus_status *rsp);
    	/** @brief Get the Hit Model state.
    	 *
    	 * @note This handler is mandatory.
    	 *
    	 * @param[in] srv Server instance to get the state of.
    	 * @param[in] ctx Message context for the message that triggered the
    	 * change, or NULL if the change is not coming from a message.
    	 * @param[out] rsp Response structure to be filled.
    	 */
    	void (*const get)(struct bt_mesh_vendor_hit_srv *srv,
    			  struct bt_mesh_msg_ctx *ctx,
    			  struct bt_mesh_vendor_playerstatus_status *rsp);
    };
    
    /**
     * Generic Vendor Hit Server instance. Should primarily be initialized with the
     * @ref BT_MESH_VENDOR_HIT_SRV_INIT macro.
     */
    struct bt_mesh_vendor_hit_srv {
    	/** Transaction ID tracker. */
    	struct bt_mesh_tid_ctx prev_transaction;
    	/** Handler function structure. */
    	const struct bt_mesh_vendor_hit_srv_handlers *handlers;
    	/** Access model pointer. */
    	struct bt_mesh_model *model;
    	/** Publish parameters. */
    	struct bt_mesh_model_pub pub;
    	/* Publication buffer */
    	struct net_buf_simple pub_buf;
    	/* Publication data */
    	uint8_t pub_data[BT_MESH_MODEL_BUF_LEN(
    		BT_MESH_VENDOR_HIT_STATUS_OP, BT_MESH_VENDOR_HIT_MSG_STATUS_MAX_LEN)];
    	/** Internal flag state. */
    	atomic_t flags;
    };
    
    
    /** @cond INTERNAL_HIDDEN */
    extern const struct bt_mesh_model_op _vendor_hit_srv_opcode_list[];
    extern const struct bt_mesh_model_cb _bt_mesh_vendor_hit_srv_cb;
    /** @endcond */
    
    #endif
    
    
    

    Above code is the "Hit-Server"

    #ifndef VENDOR_MODEL_COMMON_H
    #define VENDOR_MODEL_COMMON_H
    
    #include <string.h>
    #include <zephyr/bluetooth/mesh/msg.h>
    #include <zephyr/bluetooth/mesh.h>
    #include <bluetooth/mesh/model_types.h>
    
    #define BT_MESH_MODEL_ID_VENDOR_HIT_CLI 0x0011 //According to Nordic, this model ID is not yet taken.
    #define BT_MESH_MODEL_ID_VENDOR_HIT_SRV 0x0012 //According to Nordic, this model ID is not yet taken.
    
    // HIT
    //#define BT_MESH_VENDOR_HIT_GET_OP BT_MESH_MODEL_OP_3(0x21, VENDOR_COMPANY_ID)
    //#define BT_MESH_VENDOR_HIT_SET_OP BT_MESH_MODEL_OP_3(0x14, VENDOR_COMPANY_ID)
    //#define BT_MESH_VENDOR_HIT_STATUS_OP BT_MESH_MODEL_OP_3(0x15, VENDOR_COMPANY_ID)
    #define BT_MESH_VENDOR_HIT_GET_OP BT_MESH_MODEL_OP_2(0x82, 0x01)
    #define BT_MESH_VENDOR_HIT_SET_OP BT_MESH_MODEL_OP_2(0x82, 0x02)
    #define BT_MESH_VENDOR_HIT_STATUS_OP BT_MESH_MODEL_OP_2(0x82, 0x04)
    
    //Define minimum message length in bytes
    #define BT_MESH_VENDOR_HIT_MSG_GET_MIN_LEN 0
    #define BT_MESH_VENDOR_HIT_MSG_SET_MIN_LEN 2
    #define BT_MESH_VENDOR_HIT_MSG_STATUS_MIN_LEN 2
    
    
    // Define maximum message length in bytes
    #define BT_MESH_VENDOR_HIT_MSG_GET_MAX_LEN 0
    #define BT_MESH_VENDOR_HIT_MSG_SET_MAX_LEN 3
    #define BT_MESH_VENDOR_HIT_MSG_STATUS_MAX_LEN 3

    The last part contains the model, opcode and message length defines.

    As you can see, it follows the general example setup. It works by using the SIG opcodes (Macro BT_MESH_MODEL_OP_2) but not when switching to the BT_MESH_MODEL_OP_3 macro.

    Then I always get the error messages described in my first post.

    Looking forward to your comments.

    Best

  • Anyone able to help or has any idea on how to "debug" this?

Related