Specification
Most parts of the protocol should not be modified to enable interoperability. Some of the protocol decisions are found on this page and ongoing discussions will show whether some of these specifications should be changed.
Advertising Packets
BlueRange Mesh uses a number of advertising packets, the most important one being the JOIN_ME packet that is used for discovery of nearby nodes that also run BlueRange Mesh.
There are two types of advertising packets used by BlueRange Mesh. One uses the manufacturer specific advertisement structure, the other uses the serviceData advertisement structure. Some devices such as iOS devices have a better reception rate when using the serviceData structure.
Advertising Packet with Manufacturer Specific Data
Bytes | Type | Name | Description |
---|---|---|---|
3 |
advStructureFlags |
Flags |
Mandatory Flags Adv Structure |
4 |
advStructureManufacturer |
Manufacturer Data |
Manufacturer Specific Adv Structure with 0x024D as company identifier. |
1 |
u8 |
meshIdentifier |
Identifies this as a mesh advertising packet |
2 |
NetworkId |
networkId |
The network ID of this node |
1 |
u8 |
messageType |
Used to identify a number of different advertising messages within the mesh |
Manufacturer ID
The company ID 0x024D (M-Way Solutions GmbH) is registered with the Bluetooth SIG and may be used by any implementation that conforms to the latest specifications of the BlueRange Mesh protocol.
Mesh Identifier
The mesh identifier is 0xF0. No other identifier shall be used. Future versions might change this identifier. The purpose of the mesh identifier is to enable different protocols under the same manufacturer ID.
JOIN_ME Packet (MessageType 1)
The JOIN_ME packet contains all the information that other nodes can use to determine their best connection partner. The messageId in the header must be set to 1. Future JOIN_ME packets can have a different messageId and different values that can then be used in the Cluster Score Function. The current implementation uses the clusterSize and the number of freeIn and freeOut connections.
Bytes | Type | Name | Description |
---|---|---|---|
11 |
advHeaderManufacturerSpecific |
header |
The above depicted manufacturer specific header |
2 |
NodeId |
sender |
The nodeId of the sending node |
4 |
ClusterId |
clusterId |
Consists of the founding node’s ID and the connection loss / restart counter |
2 |
ClusterSize |
clusterSize |
The number of nodes in this cluster |
3 bit |
u8 : 3 |
freeMeshInConnections |
Number of free in connections (as peripheral) |
5 bit |
u8 : 5 |
freeMeshOutConnections |
Number of free out connections (as central) |
1 |
u8 |
batteryRuntime |
Contains the expected runtime of the device (1-59=minutes, 60-83=1-23hours, 84-113=1-29days, 114-233=1-119months, 234-254=10-29years, 255=infinite) |
1 |
i8 |
txPower |
Power of two’s complement in dBm |
1 |
u8 |
deviceType |
cf. Device Types |
2 |
u16 |
hopsToSink |
Number of hops to the shortest sink |
2 |
u16 |
meshWriteHandle |
The GATT handle for the mesh communication characteristic |
4 |
ClusterId |
ackField |
Contains the acknowledgement from another node for the slave connection procedure |
Advertising Packet With Service Data
Another type of advertising packet used by BlueRange Mesh uses the Service Data Adv Structure. This is important when dealing with mobile devices since some have hardware filtering with no support for manufacturer specific data.
Bytes | Type | Name | Description |
---|---|---|---|
Mandatory data |
|||
3 |
Flags |
advStructureFlags |
(len, type, flags byte) |
4 |
Service UUID complete |
advStructureUUID16 |
(len, type, 2 byte UUID 0xFE12) |
4 |
Service Data header |
advStructureServiceData |
(len, type, 2 byte UUID 0xFE12) |
CustomDataHeader |
|||
2 |
u16 |
messageType |
Used to determine between different messages. |
List of MessageTypes for Service Data Advertisements
Below is a list of all messages that we have currently openly specified. These all use the M-Way Solutions GmbH Service UUID 0xFE12 and the messageType is determined by checking the first two bytes of the Service Data payload.
Message Type | Description |
---|---|
0x01 |
Placeholder for JOIN_ME Packet (Currently based on Manufacturer Data) |
0x02 |
Legacy Asset Tracking Broadcast (Deprecated for new type 0x04) |
0x03 |
|
0x04 |
Legacy Asset Tracking Broadcast (Deprecated for new types 0x06 and 0x07) |
0x05 |
Sensor Broadcast |
0x06 |
Asset BLE Broadcast |
0x07 |
Asset INS Broadcast |
Other Advertising Packets
BlueRange Mesh can be used to distribute all advertising packets that conform to the BLE specification. These can be Eddystone, iBeacon or any other kind of advertising messages. These are however not essential for BlueRange Mesh itself and are therefore not documented here. Have a look at the BeaconingModule for more information.
Connection Packets
The mesh uses a number of packets that are sent over connections. Most packets that are sent over connections must have this header. There are some exceptions to this (e.g. split packets use a two byte message header for less overhead. The messageType is used to identify if the connPacketHeader is used or not.
Modules
BlueRange Mesh uses a concept of modules to group functionality into different parts. This works together nicely with the featuresets. A user is able to write his own modules that extend the basic functionality of BlueRange Mesh. Each module is identified using a module id.
ModuleIds
There are two types of module ids: the standard ModuleId has a size of one byte and is solely used by standardized modules. The VendorModuleId on the other hand uses 4 bytes to allow different vendors to develop modules that can run together in a single network without clashing with other modules that were written by different vendors on different nodes in the same mesh network. This is done by including a vendor id as part of the VendorModuleId. This vendor id must be set to the company identifier that can be acquired from the BLE SIG. See this page for instructions on how to get this company identifier and for a complete list of known company identifiers.
VendorModuleIds were not part of BlueRange Mesh prior to version 1.x.x. BlueRange Mesh tries to keep the usage of VendorModuleIds as easy as possible and does most of the work when using VendorModuleIds. Some parts will e.g. use different message types for messages with ModuleId and VendorModuleId and other parts use the ModuleIdWrapper to be able to work with both module id types in the same data structure. As a convention, whenever a ModuleId is printed out as a string, it is printed as a decimal number, e.g. 123
. VendorModuleIds on the other hand are printed as a string like this "0xABCD01F0"
to make the individual parts easier to read.
To get started, see Implementing a Custom Module.
In the implementation, the ModuleId is either used as a single byte or stored using the 4 byte ModuleIdWrapper with the subId and vendorId set to 0. This depends on the implementation of a feature.
The VendorModuleId is always composed of 4 bytes can be put together using the ModuleIdWrapper.
Bytes | Type | Name | Description |
---|---|---|---|
1 |
u8 |
prefix |
This must be set to VENDOR_MODULE_ID_RESERVED(0xF0) to be recognized as a VendorModuleId |
1 |
u8 |
subId |
The user can use this to specify up to 254 different modules with his vendorId. 0x00 and 0xFF are reserved |
2 |
u16 |
vendorId |
The company identifier as assigned by the BLE SIG |
Module Packet Header
Modules use an bigger message header to guarantee that there are no collisions between different functionality. The following describes the format of the header:
Bytes | Type | Name | Description |
---|---|---|---|
5 |
header |
MessageType must be one e.g. of the above. |
|
1 or 4 |
u8 or u32 |
moduleId or vendorModuleId |
Either a ModuleId of a core module or the VendorModuleId of a vendor specific module. |
1 |
u8 |
requestHandle |
A handle that can be used e.g. like a counter. Responses will always be returned with the same handle given in the request. |
1 |
u8 |
actionType |
This is the type of action that should be executed by the module. An individual list of subCommands is available for each of the messageTypes given above. E.g. there could be a |
… |
u8[] |
data |
additional payload data for the command |
The connPacketModule differs from the connPacketModuleVendor header in the size of the module id. They can be differentiated by checking the first byte, which is set to ModuleId::VENDOR_MODULE_ID_PREFIX (0xF0) in case of a VendorModuleId. While the connPacketModule has a size of 8 bytes, the connPacketModuleVendor needs 11 bytes.
Module Packet Header for Actions / Responses and Events
The most common usage of the connPacketModule header is to provide the possibility to trigger actions, get responses and to fire events. This is a command and response based schema that is very well suited to communicate between different nodes in a mesh network. The following MessageTypes are used for this:
MessageType | Name | Description |
---|---|---|
51 / 0x33 |
MODULE_TRIGGER_ACTION |
A request for a node to perform an action |
52 / 0x34 |
MODULE_ACTION_RESPONSE |
Response message for a previous request |
53 / 0x35 |
MODULE_GENERAL |
An event that does not need a response |
Actuator and Sensor Messages
Often, BlueRange Mesh must tunnel a different protocol such as Modbus because it is installed on a controller that is attached to a 3rd party controller. In this case, it is necessary to have a tunneling protocol that is generic enough to provide access to all functionality while still allowing a platform or MeshGateway to interpret the received data in a useful manner. This is why we have introduced the component_sense
and component_act
message types. You should use them whenever you report sensor data or when you want to write data to the node that should be written to a specific "address".
MessageType | Name | Description |
---|---|---|
58 / 0x3A |
COMPONENT_ACT |
A request to trigger an actuator |
59 / 0x3B |
COMPONENT_SENSE |
A response from a sensor |
Take a look at the detailed documentation for Sensors and Actuators.
Raw Data
Another use-case is to tunnel any kind of data through BlueRange Mesh where the nodes do not need to parse or process the data at all, e.g. a smartphone might want to send data to a MeshGateway or Backend. This could also be data of an entirely different protocol such as HTTP, etc… This could be a small data packet or a rather large file that needs to be split into several parts. To do this, we have introduced raw_data
and raw_data_light
which are documented in a lot of detail at the Raw Data page.
MessageType | Name | Description |
---|---|---|
54 / 0x36 |
MODULE_RAW_DATA |
Used to transmit large files or large amounts of data (acknowledged, similar to TCP) |
55 / 0x37 |
MODULE_RAW_DATA_LIGHT |
Used to transmit up to "MAX_MESH_PACKET_SIZE" bytes of data (see |
Module Configuration Messages
Another MessageType is dedicated to configure modules and to get more information about them.
MessageType | Name | Description |
---|---|---|
50 / 0x32 |
MODULE_CONFIG |
Used to retrieve or set a module configuration or get more information about modules |
Node IDs
A NodeId is a way of addressing devices in a network. Each device in a network must have a unique nodeId assigned to it that must not clash with the node ID of another device.
There are different node ID ranges that are used for different purposes:
Name | Node ID | Usage |
---|---|---|
NODE_ID_BROADCAST |
0 |
Broadcast address to reach all nodes in a network |
NODE_ID_DEVICE_BASE |
1…1999 |
Uniquely address devices (nodes, sinks, …) given by enrollment |
NODE_ID_VIRTUAL_BASE |
2000…19999 |
Virtual addresses to address smartphones connected to the mesh (dynamically assigned, aka. virtual partner id) |
NODE_ID_GROUP_BASE |
20000…20999 |
Address groups of devices (statically assigned at compile time) |
NODE_ID_DYNAMIC_GROUP_BROADCAST_RESERVED |
21000 |
Currently unused |
NODE_ID_DYNAMIC_GROUP_BASE |
21001…21999 |
Address a group of devices, the groups are dynamically assigned at run time and saved persistently |
NODE_ID_LOCAL_LOOPBACK |
30000 |
Address for the current node itself (similar to localhost) |
NODE_ID_HOPS_BASE |
30001…30999 |
Specify the number of hops that a packet can travel. Used for sink routing. (30 001 e.g. specifies that the packet must only reach the direct neighbours) |
NODE_ID_SHORTEST_SINK |
31000 |
Used to send a packet to sink nodes (the closest one if sink routing is enabled or all if disabled) (see Device Types) |
NODE_ID_APP_BASE |
32000 |
NodeId given to non-mesh devices that can connect via a MeshAccessConnection (e.g. a Smartphone). This is replaced by a virtual partner id during communication. |
NODE_ID_GLOBAL_DEVICE_BASE |
33000…57999 |
Assign nodeIds uniquely over multiple meshes for the same organization (used for assets that roam between different networks) |
NODE_ID_INVALID |
65535 |
Invalid node ID, which is used for internal errors |
- |
others |
All other node IDs are currently reserved |
Serial Numbers / SerialNumberIndex
The serial numbers use a special alphabet of ASCII characters that is easily readable (BCDFGHJKLMNPQRSTVWXYZ123456789
) and will not result in funny words because it does not contain vocals. Serial numbers are either 5 or 7 characters long. Each serial number can be converted to a serialNumberIndex which is represented as an unsigned 32 bit integer. If a serial number is human readable, it can be printed in its ASCII representation, but if sending a serialNumber over the network or using it in code, the serialNumberIndex should be used. Check out the appropriate methods (GenerateBeaconSerialForIndex and GetIndexForSerial) in the Utility class on how to convert the serial number. Also see the appropriate tests in the TestUtility class.
There are two types of serial number ranges. There is a range of two billion serial numbers that is exclusively managed by M-Way Solutions. This range contains serial numbers with 5 characters and also serial numbers with 7 characters. Parts of this range are licensed to partners. The second range can be used for projects working with the open source version of BlueRange Mesh completely free of charge. This is always a 7 character serial number.
M-Way Solutions Proprietary Serial Number Range
The serialNumberIndex of this range starts with 0 for the serial number BBBBB
and increments up to 0x7FFFFFFF
for the serial number D8PJQ8K
. This range should not be used without consulting us beforehand because serial numbers will clash otherwise. We assign sub-ranges to our partners which allows them to use serial numbers with 5 characters.
Open Source Testing Range
To provide beginners with an easy method for testing BlueRange Mesh, we decided to open source a part of our proprietary range that contains nicely readable serial numbers. This range starts from 2673000
(FMBBB
) and ranges until 2699999
(FM999
).By default, the serial number is generated randomly (from the unique deviceId stored in the chipset) within this testing range if there is no UICR data available (see generateRandomSerialAndNodeId in the Config class). This makes it easier to start testing BlueRange Mesh. You should not distribute products that use a serial number from this range as it will clash with others.
Open Source Serial Number Range for Products
The open sourced serial number range that you can use for final products uses serial numbers with 7 characters to avoid a clash between different vendors. A serial number is constructed by setting the most significant bit of the serialNumberIndex to 1. The next 16 bits must be set to a two byte company identifier assigned by the Bluetooth SIG. The remaining 15 bits are used to generate the individual serial numbers for this vendorId. This gives each vendor the possibility to generate around 32 thousand unique serial numbers. (See VendorSerial type).
If you do not have a Bluetooth SIG company identifier yet, you can use the manufacturer id from M-Way Solutions (0x024D
). This range starts from 0x81268000
(D9HCK3N
) and ranges until 0x8126FFFF
(D9HDSHW
). You should not publicly distribute products that use this serial number range as it could clash with other serial numbers. If you want to distribute a product, you should register with the Bluetooth SIG to get a free company identifier that you can set as the MANUFACTURER_ID in the configuration.
Encryption Keys
There are a number of different keys used throughout BlueRange Mesh. These are all 128-bit keys and are used for AES encryption between the nodes, as well as for communication with smartphones or other devices.
No Key (FM_KEY_ID_ZERO = 0)
Can only be used if a node is not enrolled and uses a key filled with all 0x00 for encryption.
Node Key (FM_KEY_ID_NODE = 1)
This key is used for the lifetime of a device and is uniquely generated during production. It must be kept secure because it allows full configuration access, e.g. enrolling and removing the enrollment.
Network key (FM_KEY_ID_NETWORK = 2)
The network key is shared between all nodes that belong to a mesh network. Whoever is in posession of this key can configure all nodes in the network and can send any message. It is important to keep this key secret, but it is possible to change it if it is compromised.
UserBase Key (FM_KEY_ID_BASE_USER = 3)
This is a key that can’t be used to connect. It is used to derive all other user keys.
Organization Key (FM_KEY_ID_ORGANIZATION = 4)
The organization key is shared between all networks of an organization. It allows access to a limited set of functionality, e.g. necessary for tracking assets between differen meshes. If the organization key leaks, it is necessary to reconfigure all meshes of the organization.
Restrained Key (FM_KEY_ID_RESTRAINED = 5)
The restrained key is generated based on the node key. It is a node key with limited access rights.
The "restrained key" can be derived from the node key by using an AES-128 bit encryption by encrypting the ASCII-String "RESTRAINED_KEY00" (without terminating 0) using the node key as the AES key. Example values are:
Node Key | Restrained Key |
---|---|
00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF |
2A:FC:35:99:4C:86:11:48:58:4C:C6:D9:EE:D4:A2:B6 |
FF:EE:DD:CC:BB:AA:99:88:77:66:55:44:33:22:11:00 |
9E:63:8B:94:65:85:91:99:A9:74:7D:A7:40:7C:DD:B3 |
DE:AD:BE:EF:DE:AD:BE:EF:DE:AD:BE:EF:DE:AD:BE:EF |
3C:58:54:FC:29:96:00:59:B7:80:6B:4C:78:49:8B:27 |
00:01:02:03:04:05:06:07:08:09:0A:0B:0C:0D:0E:0F |
60:AB:54:BB:F5:1C:3F:77:FA:BC:80:4C:E0:F4:78:58 |
User Keys (FM_KEY_ID_USER_DERIVED_START = 10 to UINT32_MAX / 2)
The user base key is used to generate millions of user keys that can be given to users or user groups. A user key allows access to a limited set of commands and can be restricted in functionality depending on the use case. If the userBaseKey leaks, all userKeys have to be regenerated and distributed to users.
A key that is filled with 0xFF is considered invalid and cannot be used. |
QR Codes
One convenient method to transmit a node key out of band is by using QR codes. Please see our dedicated specification on QR codes for more information on how to generate QR codes that are compatible with the BlueRange Mesh / BlueRange ecosystem.
Device Types
There are different device types that are given to nodes with specific functionality:
DeviceType | Name | Description |
---|---|---|
0 |
DEVICE_TYPE_INVALID |
Not used |
1 |
DEVICE_TYPE_STATIC |
A node that is installed somewhere with a position that will not change much over time. |
2 |
DEVICE_TYPE_ROAMING |
A node that can move around freely. |
3 |
DEVICE_TYPE_SINK |
A node that is installed at a fixed place and collects all the data (typically a MeshGateway). The target of sink routing. |
4 |
DEVICE_TYPE_ASSET |
A node that moves around and broadcasts its presence so that it can be detected by a mesh. |
5 |
DEVICE_TYPE_LEAF |
A node that will only connect to the mesh as a leaf but will not relay any data (Useful if its position changes but it needs a constant data connection) |
For more explanation, see Device Types.
UICR
The UICR is a special persistant storage that is used to store factory defaults once a node is flashed. The NRF_UICR→CUSTOMER area is used to store the data on nRF chips.
If you want to store a serial number, nodeKey, etc. for a node, you must write the UICR during flashing. The NRF_UICR→CUSTOMER area is used for that purpose and starts at 0x10001080. You can use srec_cat to produce a .hex file containing the desired UICR data. This can then be merged with the SoftDevice and Application or you can flash each one separately. For detailed instructions, see our Developers chapter.
BlueRange Mesh will boot with random data (random nodeId / serialNumber / …) if no data is present in the UICR. The data will however be persistent across reboots as it is generated according to the internal chip id from the FICR. Layout of UICR memory:
Offset | Size (Bytes) | Name | Description |
---|---|---|---|
0 |
4 |
MAGIC_NUMBER |
Must be set to 0x00F07700 when UICR data is available |
4 |
4 |
BOARD_TYPE |
Accepts an integer that defines the hardware board that BlueRange Mesh should be running on (boardId, a.k.a. boardType) |
8 |
8 |
SERIAL_NUMBER |
Deprecated: This contained the given serial number as ASCII (zero terminated) but is not used anymore (since 12.05.2020). Must now be set to FFFF….FFFF. The serial number is instead calculated from the SERIAL_NUMBER_INDEX |
16 |
16 |
NODE_KEY |
Should be securely and randomly generated |
32 |
4 |
MANUFACTURER_ID |
Set to manufacturer ID according to the BLE company identifiers |
36 |
4 |
DEFAULT_NETWORK_ID |
0: unenrolled; 1: using an enrollment network; other: default enrollment |
40 |
4 |
DEFAULT_NODE_ID |
Node ID to be used while not enrolled |
44 |
4 |
DEVICE_TYPE |
Type of device according to Device Types |
48 |
4 |
SERIAL_NUMBER_INDEX |
Unique index that represents the serial number |
52 |
16 |
NETWORK_KEY |
Default network key if pre-enrollment is used |
Heap usage
Heap usage (malloc / new) is prohibited in the BlueRange Mesh codebase. To ensure that this rule is followed, a linker flag for ld is used that generates a linker error if malloc is used. The error looks something like this:
Make: new_op.cc:(.text._Znwj+0xe): undefined reference to `__wrap_malloc'
If this happened to you, you have to remove the malloc / new usage.