Bluetooth mesh network

The Ultimate Bluetooth Mesh Tutorial (Part 6)

Now that we’ve covered the basics of Bluetooth mesh and also gone over how to run a simple example on the nRF52 platform, it’s time for us to dig deeper into the source code of each type of device/node involved in a mesh network.

If you missed the previous posts in this series, you can find them all here:

  • Bluetooth mesh tutorial (part 1): covering the basics as well as some of the different terminologies such as Nodes, Elements, States, Properties, Messages, Addresses, Publish/Subscribe, and Managed Flooding.
  • Bluetooth mesh tutorial (part 2): covering terminologies such as Models, Scenes, Relay nodes, Proxy nodes, Friend nodes, Low power nodes, and the architecture of Bluetooth mesh.
  • Bluetooth mesh tutorial (part 3): covering the concept of Provisioning and how security is handled in Bluetooth mesh.
  • Bluetooth mesh tutorial (part 4): covering the nRF5 SDK for Mesh in addition to the prerequisites and requirements for running a Bluetooth mesh example for the Nordic nRF52 platform.
  • Bluetooth mesh tutorial (part 5): covering setup and running a Light Switch-Light Bulb example on the nRF52 platform.

In today’s post, we’ll go over part of the source code for the example we demoed in Part 5 to better understand how to implement a Bluetooth mesh example (and be able to modify it to satisfy your own requirements).

Demo Application

General Description

This demo application implements the Generic OnOff Client and Generic OnOff Server mesh models. These are the simplest mesh models that exist in the standard, and they represent a simple On or Off state.

The Server holds the state, keeps track of it, and acts based on its value.

The Client, on the other hand, sends the messages to Set or Get the state value.

A value of 0x00 represents the Off state and a value of 0x01 represents the On state.

Source Code

The example we’re working with is provided by Nordic Semiconductor in the nRF5 SDK for Mesh. The location of the example and the source code is as follows:

└── nrf5_SDK_for_Mesh_v2.2.0_src/
│          ├── examples
│                      ├── light_switch
│                                  ├── proxy_client
│                                  ├── proxy_server
│                                  ├── provisioner

Node Types Used

The example we demoed in last week’s post included three types of nodes:

  • A Proxy Client node (the “Light Switch” implemented on an nRF52840 development board)
  • A Proxy Server node (the “Light Bulb” implemented on an nRF52840 development board)
  • A Provisioner node (a mobile phone or another nRF52 development board)

In a previous post within this series, we went over the different types of nodes in Bluetooth mesh.

Two of these nodes were Low Power nodes (LPN) and Friend nodes.

Both of these nodes are not yet implemented in the Nordic nRF5 SDK for Mesh (as of version 2.2.0). These nodes are optional in a Bluetooth mesh network implementation, so they are not mandatory and may get included in a future version of the Mesh SDK.

Let’s go over the source code used in proxy client and proxy server nodes to get a much deeper understanding of the implementation of Bluetooth mesh on the nRF52 platform.

In the next post, we’ll continue by covering the provisioner node.

Proxy Client Node

Let’s go over the main elements in the source code for the proxy client node.

Whenever you’re trying to understand the inner workings of an embedded application, the main() function (usually in main.c) is the best place to start. Here’s the main() function:

You’ll notice three main function calls:

  • initialize()
  • execution_start(start)
  • (void)sd_app_evt_wait()

Let’s go over each of these.


This is a static (local) function with the following source code:

Here we:

  • Enable the logging module, configure it, and display the first log message  "----- BLE Mesh Light Switch Client Demo -----"  [Lines 3-4].
  • Enable the timer module, the LEDs, and the buttons [Lines 6-11].
  • Call an internal SDK function that enables the SoftDevice and the reporting of events to our application [Lines 12-13].
  • Execute a workaround needed for the S140 SoftDevice (which is for the nRF52840 development board) [Line 15].
  • Set the different configurations for the BLE stack within the SoftDevice. This includes the number of links (peripherals and centrals configured), the MTU size, the number of vendor-specific UUIDs (for services and characteristics), etc. The ram_start  variable is used to return the RAM start location which gets used in the next function call [Lines 18-21].
  • Enable the BLE stack [Lines 23-24].
  • Configure the advertised device name ( "nRF5x Mesh Switch") and different connection parameters (including preferred minimum and maximum connection intervals, slave latency, and connection supervision timeout) via two local functions:  gap_params_init() and conn_params_init() [Lines 26-27].


This function call takes a function pointer (in this case start() ) and makes sure the critical sections are disabled until it completes execution (to prevent any interrupts while running the function). Let’s take a look at the  start() function:

In this function, we:

  • Enable RTT functionality to allow interaction with the application via sending commands to the serial port [Line 3].
  • Start the mesh stack. This operation involves generating a device UUID, configuring the stack, as well as assigning the necessary callback functions to handle the different mesh events [Line 4].
  • Configure and start the beaconing operation that allows the device to be provisioned (if it has not been provisioned) [Lines 6-16].
    This operation includes beaconing via the advertising bearer as well as the GATT bearer (since we are running a proxy node).
  • Get this device’s (unique) UUID from the mesh stack [Line 18].
  • Set the status of the different LEDs [Line 21].
  • Blink the LEDs a certain number of times ( LED_BLINK_CNT_START ) with a delay of LED_BLINK_INTERVAL_MS  [Line 22].


This is an internal SDK function basically puts our application in an idle state until any events are reported by the stack and need to be handled by the application.

Callback Functions

The rest of the code in this application is in callback functions that get called whenever an event is reported by the stack and needs to be handled by the application. The most important are:

  • provisioning_complete_cb():

    • Gets called when the provisioning process is successfully complete.
    • Restores the connection parameters.
    • Flashes the LEDs to indicate that the provisioning process is complete.
  • app_generic_onoff_client_status_cb():

    • This reports a status message sent to the Client.
    • The status contains the sender (Server), the present state value, the target state value, as well as any remaining time if the operation is to be performed at a later time.
  • config_server_evt_cb():

    • Reports any events intended to be processed by the Server. This includes the Node Reset message which is sent when the device is removed from the network (unprovisioned).
    • In the case the Node Reset message is received, the server resets the mesh stack via the local  node_reset() function:
  • button_event_handler():

    • This function handles 4 buttons used by two Clients in the following manner:
      • Buttons #1 and #2 send the On and Off message as Client #1, respectively.
      • Buttons #3 and #4 send the On and Off message as Client #2, respectively.
      • Buttons #1 and #2 demonstrate sending a “reliable” message that requires the receiver to acknowledge receipt of the message.
      • Buttons #3 and #4 demonstrate sending an unacknowledged message that does not require a response from the recipient.
    • It also sets the LEDs on the local development board to the indicated state (to give feedback to the user that an operation message has been sent to the Server). For example, pressing Button #1 will send the On message to the Server but will also turn On LED #1 to indicate to the user that the operation was performed.
  • rtt_input_handler():

    • Allows the user to interact and trigger button presses by sending commands via the serial terminal instead of a physical button press.
  • models_init_cb():

    • This function handles initializing the appropriate models for the application. In this case, we are initializing two Generic OnOff Clients [Line 12].
    • If you need to implement additional models, this would be the place to do so.

Proxy Server Node

The Proxy Server Node source code is almost identical to the Proxy Client Node code with a few noteworthy exceptions:

  • models_init_cb():

    which calls app_model_init() :

    • Here, the model initialized is the Generic OnOff Server model instead of the Generic OnOff Client model.
  • button_event_handler():

    • Button #1 turns on LED #1 on the local development board and at the same time triggers a status message sent to the Client Node to notify it of the status change.
    • Button #3 is used to reset the node and “unprovision” it from the network.
  • The start()  function does not include blinking of the LEDs as in the Proxy Client Node example code.
  • The implementation of two functions:
    • app_onoff_server_set_cb()  for setting the LED status:
    • app_onoff_server_get_cb() for getting the LED status:


In this post, we covered the source code for two of the nodes within the mesh network:

  • Proxy client node
  • Proxy server node

We talked about how the Generic OnOff model gets initialized and configured for each of the client and server nodes.

In this example, which we tested in the previous post, we used a mobile phone with the nRF Mesh application as the provisioner. However, the nRF5 SDK for Mesh also provides a provisioner example that runs on the nRF52 development kit. In next week’s post, we’ll test the nRF-based provisioner device and take a look at the source code for this node.

Be sure to enter your email in the form below to be notified when the next tutorial gets published!


Leave a Comment