What is a Model in Bluetooth Mesh?
Every smart device in a Bluetooth Mesh network exposes its behaviour through models. A model defines exactly which messages a device can send or receive and what it does with them. Think of a model like a contract — it says “I support these operations and nothing else.”
Models are the building blocks on top of which all mesh product interoperability is built. A light bulb from one vendor and a switch from another can work together purely because they both implement the same SIG-adopted models.
Key Terms
The Three Model Roles
A server model holds state and responds to messages that read or change that state. A light bulb implements a Generic OnOff Server — it stores whether it is on or off and reacts when it receives a Generic OnOff Set message.
The server never initiates communication about its own state; it waits for a client to ask or command.
A client model sends commands or requests to servers. A wall switch implements a Generic OnOff Client — it sends Set or Get messages but does not hold the actual on/off state itself.
Client models are stateless from the mesh perspective; they drive servers but own no persistent state.
A control model plays both roles at the same time. It acts as a client towards some devices and as a server towards others. A lighting controller is the classic example:
- Acts as a client to occupancy sensors (reads sensor data)
- Acts as a client to luminaires (sends dimming commands)
- Acts as a server to a smartphone settings app (exposes configuration state)
The lighting controller may be built into the sensor, built into the luminaire, or sit as a dedicated standalone device — all three are valid deployments.
Root Models vs Extended Models
A root model does not depend on any other model. It is self-contained. Most foundation models defined by Bluetooth SIG are root models.
An extended model builds on top of a root model. When a model specification says it extends another model, both models must be instantiated together on the same node. The extended model adds new states or new messages but cannot remove anything the root model already defines.
This is the mesh way of doing “inheritance” — except it is additive only, never subtractive.
Model Immutability — Why There Are No Versions
Once a model is published by Bluetooth SIG, it cannot be changed. You cannot add optional features to it, remove a mandatory message, or patch its behaviour. There are no version numbers and no feature flags on models.
If new behaviour is needed, a new extended model is written. The original model continues to exist and work exactly as before. Devices that only know the old model keep working; devices that support the new extended model get the extra capability.
The key benefit: knowing which models an element supports tells you the exact behaviour of that element — no guessing, no negotiation.
SIG Adopted Models vs Vendor Models
Bluetooth SIG defines standard models with a 16-bit identifier. These are interoperable across all vendors. For example, the Generic OnOff Server has SIG Model ID 0x1000.
/* ── BlueZ mesh/mesh-defs.h ── */
/* Generic OnOff Server — SIG Adopted, 16-bit model ID */
#define GENERIC_ONOFF_SERVER_MODEL_ID 0x1000
/* Generic OnOff Client — SIG Adopted, 16-bit model ID */
#define GENERIC_ONOFF_CLIENT_MODEL_ID 0x1001
/* Generic Level Server */
#define GENERIC_LEVEL_SERVER_MODEL_ID 0x1002
/* Light Lightness Server */
#define LIGHT_LIGHTNESS_SERVER_MODEL_ID 0x1300
Vendor-defined models use a 32-bit identifier: the upper 16 bits are the Bluetooth SIG-assigned Company ID, and the lower 16 bits are the vendor’s own model ID. This namespace prevents collisions between different vendors.
/* ── vendor model ID construction ── */
/*
* Bluetooth SIG Company ID for Texas Instruments: 0x000D
* Vendor-specific model sub-ID: 0x0001
*
* Full 32-bit vendor model ID = (company_id << 16) | model_sub_id
*/
#define TI_COMPANY_ID 0x000D
#define TI_VENDOR_MODEL_SUB 0x0001
#define TI_VENDOR_MODEL_ID ((TI_COMPANY_ID << 16) | TI_VENDOR_MODEL_SUB)
/* Result: 0x000D0001 */
/* ── BlueZ mesh/model.c — checking if a model ID is SIG or vendor ── */
static bool is_vendor(uint32_t model_id)
{
/*
* SIG adopted models fit in 16 bits (high 16 bits are 0).
* Vendor models have a non-zero company ID in the high 16 bits.
*/
return (model_id > 0xffff);
}
static uint16_t vendor_company_id(uint32_t model_id)
{
return (uint16_t)(model_id >> 16);
}
static uint16_t vendor_model_sub_id(uint32_t model_id)
{
return (uint16_t)(model_id & 0xffff);
}
Messages and Opcodes — Shared Behaviour
Each model operation is identified by an opcode. Opcodes can be 1, 2, or 3 bytes depending on whether they are SIG-defined or vendor-defined. The same message can appear in multiple models; its behaviour is identical in each — this is what allows different models to interoperate without ambiguity.
/* ── BlueZ mesh/model.c — opcode encoding ── */
/*
* Encode an opcode into the access layer payload buffer.
* Returns number of bytes written (1, 2, or 3).
*
* 1-byte opcode : 0x01 – 0x7E (SIG, most common)
* 2-byte opcode : 0x8000 – 0xBFFF (SIG, extended set)
* 3-byte opcode : 0xC00000 – 0xFFFFFF (vendor-specific)
*/
uint32_t mesh_model_opcode_set(uint32_t opcode, uint8_t *buf)
{
if (opcode <= 0x7e) {
buf[0] = opcode;
return 1;
}
if (opcode >= 0x8000 && opcode <= 0xbfff) {
put_be16(opcode, buf);
return 2;
}
if (opcode >= 0xc00000 && opcode <= 0xffffff) {
buf[0] = (opcode >> 16) & 0xff;
put_be16(opcode, buf + 1);
return 3;
}
return 0;
}
/* ── Common SIG opcodes (mesh/mesh-defs.h) ── */
/* Generic OnOff opcodes */
#define OP_GENERIC_ONOFF_GET 0x8201
#define OP_GENERIC_ONOFF_SET 0x8202
#define OP_GENERIC_ONOFF_SET_UNACK 0x8203
#define OP_GENERIC_ONOFF_STATUS 0x8204
/* Generic Level opcodes */
#define OP_GENERIC_LEVEL_GET 0x8205
#define OP_GENERIC_LEVEL_SET 0x8206
#define OP_GENERIC_LEVEL_SET_UNACK 0x8207
#define OP_GENERIC_LEVEL_STATUS 0x8208
Quick Summary
- Server — owns state, responds to messages
- Client — sends commands/queries, owns no persistent state
- Control — client + server combined in one device
- Root model — standalone, no dependency on other models
- Extended model — adds behaviour on top of a root model, both must coexist on the same node
- SIG adopted — 16-bit model ID, standardised and interoperable
- Vendor model — 32-bit ID (Company ID + Sub-ID), proprietary
- Model specs are immutable — changes always produce a new extended model
Continue to Part 2
Next: Elements, States, State Binding, and Composition Data
