Mouser Bluetooth Mesh: L7 Modifying the Dimmable Light Code to Add Another Light Element

How To Design With Bluetooth Mesh


You can "git" a workspace will all of these files at https://github.com/iotexpert/MouserVTWBluetoothMesh or git@github.com:iotexpert/MouserVTWBluetoothMesh.git

Summary

In this project I will create a Bluetooth Mesh Node that has two Light Lightness Server elements, one to control the red LED and one to control the Green LED.  The purpose is to show an example adding elements to a node.

I will start with the BLE_Mesh_LightDimmable and then modify it to add the ability to control the Green LED as well.

To implement this lesson I will follow these steps:

  1. Make New Application with BLE_Mesh_LightDimmable Template
  2. Program it to make sure that things are still working
  3. Modify led_control.h to handle two LEDs
  4. Modify led_control.c to handle two LEDs
  5. Modify light_dimmable.c to update the Bluetooth Mesh Configuration
  6. Test

Make New Project with BLE_Mesh_LightDimmable Template

Use File->New->ModusToobox IDE Application

Pick the “CYBT-213043-MESH”

Choose “BLE_Mesh_LightDimmable” and name your project “VTW_RedGreen”

Click “Finish”

Program it to make sure that things are still working by clicking “Launches -> VTW_RedGreen Build + Program”

After it is downloaded you can see that it boots.

Press “Start Scan”.  After a while you will see that it found the device.

Now press “Provision and configure”

Modify led_control.h

The files led_control.h and .c are used to manage the LED.  I will extend the led_control_set_brightness_level by adding the ability to control two LEDs – a Red and a Green

typedef enum {
	RED,
	GREEN,
} led_control_t;

void led_control_init(void);
void led_control_set_brighness_level(led_control_t whichLED, uint8_t brightness_level);

Modify led_control.c

Now I need to hook up a PWM to the Green LED.  I will use PWM1 (notice that I also change the PWM_CHANNEL to _RED and _GREEN)

#define PWM_CHANNEL_RED           PWM0
#define PWM_CHANNEL_GREEN         PWM1

The led_control_init function is used to setup the hardware.  Specifically to configure the two PWMs to be attached to the correct pins.

void led_control_init(void)
{
    pwm_config_t pwm_config;

    /* configure PWM */
    wiced_hal_gpio_select_function(WICED_GPIO_PIN_LED_2, WICED_PWM0);
    wiced_hal_gpio_select_function(WICED_GPIO_PIN_LED_3, WICED_PWM1);

    wiced_hal_aclk_enable(PWM_INP_CLK_IN_HZ, ACLK1, ACLK_FREQ_24_MHZ);
    wiced_hal_pwm_get_params(PWM_INP_CLK_IN_HZ, 0, PWM_FREQ_IN_HZ, &pwm_config);
    wiced_hal_pwm_start(PWM_CHANNEL_RED, PMU_CLK, pwm_config.toggle_count, pwm_config.init_count, 1);
    wiced_hal_pwm_start(PWM_CHANNEL_GREEN, PMU_CLK, pwm_config.toggle_count, pwm_config.init_count, 1);
}

Finally, I modify the …set_brightness… function to handle Red and Green.

void led_control_set_brighness_level(led_control_t whichLED, uint8_t brightness_level)
{
    pwm_config_t pwm_config;

    WICED_BT_TRACE("set brightness:%d\n", brightness_level);

    // ToDo.  For some reason, setting brightness to 100% does not work well on 20719B1 platform. For now just use 99% instead of 100.
    if (brightness_level == 100)
        brightness_level = 99;

    wiced_hal_pwm_get_params(PWM_INP_CLK_IN_HZ, brightness_level, PWM_FREQ_IN_HZ, &pwm_config);
    switch(whichLED)
    {
    case RED:
        wiced_hal_pwm_change_values(PWM_CHANNEL_RED, pwm_config.toggle_count, pwm_config.init_count);

    break;
    case GREEN:
        wiced_hal_pwm_change_values(PWM_CHANNEL_GREEN, pwm_config.toggle_count, pwm_config.init_count);

    	break;
    }
}

Modify light_dimmable.c

Now I need to add a new element to the mesh_elements array.  Before I can do that, it need to create new element array called “mesh_element2_models” with the model that I need.  Notice that I also change the original code to have _RED

wiced_bt_mesh_core_config_model_t   mesh_element1_models[] =
{
    WICED_BT_MESH_DEVICE,
    WICED_BT_MESH_MODEL_USER_PROPERTY_SERVER,
    WICED_BT_MESH_MODEL_LIGHT_LIGHTNESS_SERVER,
};
#define MESH_APP_NUM_MODELS_RED  (sizeof(mesh_element1_models) / sizeof(wiced_bt_mesh_core_config_model_t))




wiced_bt_mesh_core_config_property_t mesh_element1_properties[] =
{
    {
        .id          = WICED_BT_MESH_PROPERTY_DEVICE_FIRMWARE_REVISION,
        .type        = WICED_BT_MESH_PROPERTY_TYPE_USER,
        .user_access = WICED_BT_MESH_PROPERTY_ID_READABLE,
        .max_len     = WICED_BT_MESH_PROPERTY_LEN_DEVICE_FIRMWARE_REVISION,
        .value       = mesh_prop_fw_version
    },
};
#define MESH_APP_NUM_PROPERTIES (sizeof(mesh_element1_properties) / sizeof(wiced_bt_mesh_core_config_property_t))

wiced_bt_mesh_core_config_model_t   mesh_element2_models[] =
{
    WICED_BT_MESH_MODEL_LIGHT_LIGHTNESS_SERVER,
};
#define MESH_APP_NUM_MODELS_GREEN  (sizeof(mesh_element2_models) / sizeof(wiced_bt_mesh_core_config_model_t))

#define MESH_LIGHT_LIGHTNESS_SERVER_ELEMENT_INDEX_RED   0
#define MESH_LIGHT_LIGHTNESS_SERVER_ELEMENT_INDEX_GREEN   1

wiced_bt_mesh_core_config_element_t mesh_elements[] =
{
    {
        .location = MESH_ELEM_LOC_MAIN,                                 // location description as defined in the GATT Bluetooth Namespace Descriptors section of the Bluetooth SIG Assigned Numbers
        .default_transition_time = MESH_DEFAULT_TRANSITION_TIME_IN_MS,  // Default transition time for models of the element in milliseconds
        .onpowerup_state = WICED_BT_MESH_ON_POWER_UP_STATE_RESTORE,     // Default element behavior on power up
        .default_level = 0,                                             // Default value of the variable controlled on this element (for example power, lightness, temperature, hue...)
        .range_min = 1,                                                 // Minimum value of the variable controlled on this element (for example power, lightness, temperature, hue...)
        .range_max = 0xffff,                                            // Maximum value of the variable controlled on this element (for example power, lightness, temperature, hue...)
        .move_rollover = 0,                                             // If true when level gets to range_max during move operation, it switches to min, otherwise move stops.
        .properties_num = MESH_APP_NUM_PROPERTIES,                      // Number of properties in the array models
        .properties = mesh_element1_properties,                         // Array of properties in the element.
        .sensors_num = 0,                                               // Number of sensors in the sensor array
        .sensors = NULL,                                                // Array of sensors of that element
        .models_num = MESH_APP_NUM_MODELS_RED,                          // Number of models in the array models
        .models = mesh_element1_models,                                 // Array of models located in that element. Model data is defined by structure wiced_bt_mesh_core_config_model_t
    },
    {
        .location = MESH_ELEM_LOC_MAIN,                                 // location description as defined in the GATT Bluetooth Namespace Descriptors section of the Bluetooth SIG Assigned Numbers
        .default_transition_time = MESH_DEFAULT_TRANSITION_TIME_IN_MS,  // Default transition time for models of the element in milliseconds
        .onpowerup_state = WICED_BT_MESH_ON_POWER_UP_STATE_RESTORE,     // Default element behavior on power up
        .default_level = 0,                                             // Default value of the variable controlled on this element (for example power, lightness, temperature, hue...)
        .range_min = 1,                                                 // Minimum value of the variable controlled on this element (for example power, lightness, temperature, hue...)
        .range_max = 0xffff,                                            // Maximum value of the variable controlled on this element (for example power, lightness, temperature, hue...)
        .move_rollover = 0,                                             // If true when level gets to range_max during move operation, it switches to min, otherwise move stops.
        .properties_num = 0,                                            // Number of properties in the array models
        .properties = NULL,                                             // Array of properties in the element.
        .sensors_num = 0,                                               // Number of sensors in the sensor array
        .sensors = NULL,                                                // Array of sensors of that element
        .models_num = MESH_APP_NUM_MODELS_GREEN,                              // Number of models in the array models
        .models = mesh_element2_models,                                 // Array of models located in that element. Model data is defined by structure wiced_bt_mesh_core_config_model_t
    },

};

In the mesh_app_init function you need to initialize the new light lightness server model. We will use the same message handler for both light lightness server models.

    wiced_bt_mesh_model_light_lightness_server_init(MESH_LIGHT_LIGHTNESS_SERVER_ELEMENT_INDEX_GREEN, mesh_app_message_handler, is_provisioned);

Search and replace all of the “led_control_set_brighness_level(attention_brightness);” to be “led_control_set_brighness_level(RED,attention_brightness);”

The last step is to update the mesh_app_process_set_level callback to handle the two cases.

/*
 * Command from the level client is received to set the new level
 */
void mesh_app_process_set_level(uint8_t element_idx, wiced_bt_mesh_light_lightness_status_t *p_status)
{
    WICED_BT_TRACE("mesh light srv set level element:%d present actual:%d linear:%d remaining_time:%d\n",
        element_idx, p_status->lightness_actual_present, p_status->lightness_linear_present, p_status->remaining_time);

    last_known_brightness = (uint8_t)((uint32_t)p_status->lightness_actual_present * 100 / 65535);
    if(element_idx == MESH_LIGHT_LIGHTNESS_SERVER_ELEMENT_INDEX_RED)
    		led_control_set_brighness_level(RED,last_known_brightness);

    if(element_idx == MESH_LIGHT_LIGHTNESS_SERVER_ELEMENT_INDEX_GREEN)
    		led_control_set_brighness_level(GREEN,last_known_brightness);

    // If we were alerting user, stop it.
    wiced_stop_timer(&attention_timer);
}

Test

Do a “Node Reset” to remove the old node from the network.  It doesn’t really matter… but I find it less confusing to have it gone.

Program the kit using the launch “VTW_RedGreen Build + Program”

Press “Scan Unprovisioned”

Press “Provision and configure”.  It takes a minute.  When it is done you will see “…done” in the trace window.

Now I can “Control” the two LEDs.  Notice that the address of the first one is 0002 and the second is 0003.

When I do “Set” I can turn on the Green LED.

Mouser Bluetooth Mesh: L6 The Dimmable Light Code

How To Design With Bluetooth Mesh


You can "git" a workspace will all of these files at https://github.com/iotexpert/MouserVTWBluetoothMesh or git@github.com:iotexpert/MouserVTWBluetoothMesh.git

Summary

This is a programming class.  So let’s take a closer look at the Light Dimmable Project.  It is not very hard.  The project is broken up into three main files

  1. led_control.h – which is the public interface to control the LED.
  2. led_control.c – the actual functions to control the LED.
  3. light_dimmable.c – the user application part of the Bluetooth Mesh.

We will dig through these files one at a time.

led_control.h

This file is the public API for controlling the LED.  There are two functions, the first “led_control_init” which must setup the hardware for the LED.  And a function to set the brightness level.  OK that is simple enough.

#ifndef __LED_CONTROL__H
#define __LED_CONTROL__H

#ifdef __cplusplus
extern "C" {
#endif

void led_control_init(void);
void led_control_set_brighness_level(uint8_t brightness_level);

#ifdef __cplusplus
}
#endif

#endif

led_control.c

The two functions in this file are led_control_init which just sets up a PWM to control the LED.  We are using the CYW20819A1.  The wiced_had_gpio_select_function just tells the pin to connect the pin mux to PWM0.

void led_control_init(void)
{
    pwm_config_t pwm_config;

    /* configure PWM */
#ifdef CYW20719B1
    wiced_hal_pwm_configure_pin(led_pin, PWM_CHANNEL);
#endif

#ifdef CYW20819A1
    wiced_hal_gpio_select_function(WICED_GPIO_PIN_LED_2, WICED_PWM0);
#endif
    wiced_hal_aclk_enable(PWM_INP_CLK_IN_HZ, ACLK1, ACLK_FREQ_24_MHZ);
    wiced_hal_pwm_get_params(PWM_INP_CLK_IN_HZ, 0, PWM_FREQ_IN_HZ, &pwm_config);
    wiced_hal_pwm_start(PWM_CHANNEL, PMU_CLK, pwm_config.toggle_count, pwm_config.init_count, 1);
}

You can see the API documentation by clicking Help->ModusToolbox API Reference–>WICED API Reference

I look down through the documentation until I find the wiced_hal_gpio_select_function

The led_control_set_brighness_level takes an input level from 0-100 and picks out the right PWM duty cycle.

void led_control_set_brighness_level(uint8_t brightness_level)
{
    pwm_config_t pwm_config;

    WICED_BT_TRACE("set brightness:%d\n", brightness_level);

    // ToDo.  For some reason, setting brightness to 100% does not work well on 20719B1 platform. For now just use 99% instead of 100.
    if (brightness_level == 100)
        brightness_level = 99;

    wiced_hal_pwm_get_params(PWM_INP_CLK_IN_HZ, brightness_level, PWM_FREQ_IN_HZ, &pwm_config);
    wiced_hal_pwm_change_values(PWM_CHANNEL, pwm_config.toggle_count, pwm_config.init_count);
}

light_dimmable.c

There are 6 sections of the Bluetooth Mesh User Application firmware.

  1. Mesh Element/Model
  2. Mesh Core Configuration
  3. Mesh Application Callbacks
  4. mesh_app_init
  5. Attention Handler (a new concept)
  6. Light Server Handler

light_dimmable.c – Mesh Element/Model

This project will have one element, which holds three models including a property server (which has one property)

wiced_bt_mesh_core_config_model_t   mesh_element1_models[] =
{
    WICED_BT_MESH_DEVICE,
    WICED_BT_MESH_MODEL_USER_PROPERTY_SERVER,
    WICED_BT_MESH_MODEL_LIGHT_LIGHTNESS_SERVER,
};
#define MESH_APP_NUM_MODELS  (sizeof(mesh_element1_models) / sizeof(wiced_bt_mesh_core_config_model_t))

wiced_bt_mesh_core_config_property_t mesh_element1_properties[] =
{
    {
        .id          = WICED_BT_MESH_PROPERTY_DEVICE_FIRMWARE_REVISION,
        .type        = WICED_BT_MESH_PROPERTY_TYPE_USER,
        .user_access = WICED_BT_MESH_PROPERTY_ID_READABLE,
        .max_len     = WICED_BT_MESH_PROPERTY_LEN_DEVICE_FIRMWARE_REVISION,
        .value       = mesh_prop_fw_version
    },
};
#define MESH_APP_NUM_PROPERTIES (sizeof(mesh_element1_properties) / sizeof(wiced_bt_mesh_core_config_property_t))


#define MESH_LIGHT_LIGHTNESS_SERVER_ELEMENT_INDEX   0

wiced_bt_mesh_core_config_element_t mesh_elements[] =
{
    {
        .location = MESH_ELEM_LOC_MAIN,                                 // location description as defined in the GATT Bluetooth Namespace Descriptors section of the Bluetooth SIG Assigned Numbers
        .default_transition_time = MESH_DEFAULT_TRANSITION_TIME_IN_MS,  // Default transition time for models of the element in milliseconds
        .onpowerup_state = WICED_BT_MESH_ON_POWER_UP_STATE_RESTORE,     // Default element behavior on power up
        .default_level = 0,                                             // Default value of the variable controlled on this element (for example power, lightness, temperature, hue...)
        .range_min = 1,                                                 // Minimum value of the variable controlled on this element (for example power, lightness, temperature, hue...)
        .range_max = 0xffff,                                            // Maximum value of the variable controlled on this element (for example power, lightness, temperature, hue...)
        .move_rollover = 0,                                             // If true when level gets to range_max during move operation, it switches to min, otherwise move stops.
        .properties_num = MESH_APP_NUM_PROPERTIES,                      // Number of properties in the array models
        .properties = mesh_element1_properties,                         // Array of properties in the element.
        .sensors_num = 0,                                               // Number of sensors in the sensor array
        .sensors = NULL,                                                // Array of sensors of that element
        .models_num = MESH_APP_NUM_MODELS,                              // Number of models in the array models
        .models = mesh_element1_models,                                 // Array of models located in that element. Model data is defined by structure wiced_bt_mesh_core_config_model_t
    },
};

light_dimmable.c – Mesh Core Config

This configuration structure is read by the Bluetooth Mesh stack automatically.  It just tells the stack how to behave.

wiced_bt_mesh_core_config_t  mesh_config =
{
    .company_id         = MESH_COMPANY_ID_CYPRESS,                  // Company identifier assigned by the Bluetooth SIG
    .product_id         = MESH_PID,                                 // Vendor-assigned product identifier
    .vendor_id          = MESH_VID,                                 // Vendor-assigned product version identifier
    .replay_cache_size  = MESH_CACHE_REPLAY_SIZE,                   // Number of replay protection entries, i.e. maximum number of mesh devices that can send application messages to this device.
    .features           = WICED_BT_MESH_CORE_FEATURE_BIT_FRIEND | WICED_BT_MESH_CORE_FEATURE_BIT_RELAY | WICED_BT_MESH_CORE_FEATURE_BIT_GATT_PROXY_SERVER,   // In Friend mode support friend, relay
    .friend_cfg         =                                           // Configuration of the Friend Feature(Receive Window in Ms, messages cache)
    {
        .receive_window        = 200,                               // Receive Window value in milliseconds supported by the Friend node.
        .cache_buf_len         = 300,                               // Length of the buffer for the cache
        .max_lpn_num           = 4                                  // Max number of Low Power Nodes with established friendship. Must be > 0 if Friend feature is supported. 
    },
    .low_power          =                                           // Configuration of the Low Power Feature
    {
        .rssi_factor           = 0,                                 // contribution of the RSSI measured by the Friend node used in Friend Offer Delay calculations.
        .receive_window_factor = 0,                                 // contribution of the supported Receive Window used in Friend Offer Delay calculations.
        .min_cache_size_log    = 0,                                 // minimum number of messages that the Friend node can store in its Friend Cache.
        .receive_delay         = 0,                                 // Receive delay in 1 ms units to be requested by the Low Power node.
        .poll_timeout          = 0                                  // Poll timeout in 100ms units to be requested by the Low Power node.
    },
    .gatt_client_only          = WICED_FALSE,                       // Can connect to mesh over GATT or ADV
    .elements_num  = (uint8_t)(sizeof(mesh_elements) / sizeof(mesh_elements[0])),   // number of elements on this device
    .elements      = mesh_elements                                  // Array of elements for this device
};

light_dimmable.c – Mesh Application Callbacks

The Bluetooth Mesh stack  interacts with your application via callbacks.  This structure tells the stack when you are interested in being called.

/*
 * Mesh application library will call into application functions if provided by the application.
 */
wiced_bt_mesh_app_func_table_t wiced_bt_mesh_app_func_table =
{
    mesh_app_init,          // application initialization
    NULL,                   // Default SDK platform button processing
    NULL,                   // GATT connection status
    mesh_app_attention,     // attention processing
    NULL,                   // notify period set
    NULL,                   // WICED HCI command
    NULL,                   // LPN sleep
    NULL                    // factory reset
};

light_dimmable.c – mesh_app_init

This function set’s up things after the stack starts.  Specifically it configures data for the property server. It also sets up the Scan Response packet.  Finally it turns on the servers for the Property and Light Lightness Servers and registers a callback function when messages are received for the light lightness server model.

/******************************************************
 *               Function Definitions
 ******************************************************/
void mesh_app_init(wiced_bool_t is_provisioned)
{
#if 0
    extern uint8_t wiced_bt_mesh_model_trace_enabled;
    wiced_bt_mesh_model_trace_enabled = WICED_TRUE;
#endif
    wiced_bt_cfg_settings.device_name = (uint8_t *)"Dimmable Light";
    wiced_bt_cfg_settings.gatt_cfg.appearance = APPEARANCE_LIGHT_CEILING;

    mesh_prop_fw_version[0] = 0x30 + (WICED_SDK_MAJOR_VER / 10);
    mesh_prop_fw_version[1] = 0x30 + (WICED_SDK_MAJOR_VER % 10);
    mesh_prop_fw_version[2] = 0x30 + (WICED_SDK_MINOR_VER / 10);
    mesh_prop_fw_version[3] = 0x30 + (WICED_SDK_MINOR_VER % 10);
    mesh_prop_fw_version[4] = 0x30 + (WICED_SDK_REV_NUMBER / 10);
    mesh_prop_fw_version[5] = 0x30 + (WICED_SDK_REV_NUMBER % 10);
    mesh_prop_fw_version[6] = 0x30 + (WICED_SDK_BUILD_NUMBER / 10);
    mesh_prop_fw_version[7] = 0x30 + (WICED_SDK_BUILD_NUMBER % 10);

    // Adv Data is fixed. Spec allows to put URI, Name, Appearance and Tx Power in the Scan Response Data.
    if (!is_provisioned)
    {
        wiced_bt_ble_advert_elem_t  adv_elem[3];
        uint8_t                     buf[2];
        uint8_t                     num_elem = 0;

        adv_elem[num_elem].advert_type = BTM_BLE_ADVERT_TYPE_NAME_COMPLETE;
        adv_elem[num_elem].len         = (uint16_t)strlen((const char*)wiced_bt_cfg_settings.device_name);
        adv_elem[num_elem].p_data      = wiced_bt_cfg_settings.device_name;
        num_elem++;

        adv_elem[num_elem].advert_type = BTM_BLE_ADVERT_TYPE_APPEARANCE;
        adv_elem[num_elem].len         = 2;
        buf[0]                         = (uint8_t)wiced_bt_cfg_settings.gatt_cfg.appearance;
        buf[1]                         = (uint8_t)(wiced_bt_cfg_settings.gatt_cfg.appearance >> 8);
        adv_elem[num_elem].p_data      = buf;
        num_elem++;

        wiced_bt_mesh_set_raw_scan_response_data(num_elem, adv_elem);
    }
    led_control_init();

    wiced_init_timer(&attention_timer, attention_timer_cb, 0, WICED_SECONDS_PERIODIC_TIMER);

    // Initialize Light Lightness Server and register a callback which will be executed when it is time to change the brightness of the bulb
    wiced_bt_mesh_model_light_lightness_server_init(MESH_LIGHT_LIGHTNESS_SERVER_ELEMENT_INDEX, mesh_app_message_handler, is_provisioned);

    // Initialize the Property Server.  We do not need to be notified when Property is set, because our only property is readonly
    wiced_bt_mesh_model_property_server_init(MESH_LIGHT_LIGHTNESS_SERVER_ELEMENT_INDEX, NULL, is_provisioned);
}

light_dimmable.c – Attention

There are situations where the stack might want to get the user’s attention.   In the callback we setup the stack to call mesh_app_attention when it wants the user’s attention.  The parameter is how long the stack wants to alert the user.   This function starts a timer to blink the RED LED. Once the attention time has expired, the timer stops.

*
 * Mesh library requests to alert user for "time" seconds.
 */
void mesh_app_attention(uint8_t element_idx, uint8_t time)
{
    WICED_BT_TRACE("dimmable light attention:%d sec\n", time);

    // If time is zero, stop alerting and restore the last known brightness
    if (time == 0)
    {
        wiced_stop_timer(&attention_timer);
        led_control_set_brighness_level(last_known_brightness);
        return;
    }
    wiced_start_timer(&attention_timer, 1);
    attention_time = time;
    attention_brightness = (last_known_brightness != 0) ? 0 : 100;
    led_control_set_brighness_level(attention_brightness);
}

/*
 * Attention timer callback is executed every second while user needs to be alerted.
 * Just switch brightness between 0 and 100%
 */
void attention_timer_cb(TIMER_PARAM_TYPE arg)
{
    WICED_BT_TRACE("dimmable light attention timeout:%d\n", attention_time);

    if (--attention_time == 0)
    {
        wiced_stop_timer(&attention_timer);
        led_control_set_brighness_level(last_known_brightness);
        return;
    }
    attention_brightness = (attention_brightness == 0) ? 100 : 0;
    led_control_set_brighness_level(attention_brightness);
}

light_dimmable.c – Light Server Handler

When the Node receives a message that has been published to it to change the Light Lightness value, this function is called.  Basically it just calls the hardware API to change the LED brightness.

/*
 * Process event received from the models library.
 */
void mesh_app_message_handler(uint8_t element_idx, uint16_t event, void *p_data)
{
    switch (event)
    {
    case WICED_BT_MESH_LIGHT_LIGHTNESS_SET:
        mesh_app_process_set_level(element_idx, (wiced_bt_mesh_light_lightness_status_t *)p_data);
        break;

    default:
        WICED_BT_TRACE("dimmable light unknown msg:%d\n", event);
        break;
    }
}

/*
 * Command from the level client is received to set the new level
 */
void mesh_app_process_set_level(uint8_t element_idx, wiced_bt_mesh_light_lightness_status_t *p_status)
{
    WICED_BT_TRACE("mesh light srv set level element:%d present actual:%d linear:%d remaining_time:%d\n",
        element_idx, p_status->lightness_actual_present, p_status->lightness_linear_present, p_status->remaining_time);

    last_known_brightness = (uint8_t)((uint32_t)p_status->lightness_actual_present * 100 / 65535);
    led_control_set_brighness_level(last_known_brightness);

    // If we were alerting user, stop it.
    wiced_stop_timer(&attention_timer);
}

 

Mouser Bluetooth Mesh: L5 Bluetooth Mesh Fundamentals

How To Design With Bluetooth Mesh


You can "git" a workspace will all of these files at https://github.com/iotexpert/MouserVTWBluetoothMesh or git@github.com:iotexpert/MouserVTWBluetoothMesh.git

Summary

This lesson walks you through the fundamentals of Bluetooth Mesh Networking.  There is actually quite a bit going on from a detail standpoint to make Bluetooth Mesh work as documented in the 713 pages of Bluetooth Mesh specifications:

However, Cypress has abstracted a significant amount of this complexity into our stack so as to ease your journey.

Specifically in this lesson we will talk about:

  1. Topology
  2. Elements
  3. Addressing
  4. Messaging
  5. Subscribe & Publish
  6. Models
  7. State
  8. Complexity
  9. Security
  10. Provisioning & Configuration
  11. Bluetooth Mesh Stack

Bluetooth Mesh Topology

Let’s start by examining a prototypical Bluetooth Mesh network:

 

Standard Node
The standard node functionality involves sending and receiving mesh messages. Every node in the network must be able to act as a standard node.
Relay Node
Relay nodes can receive a message for the network and then retransmit it to other devices in range. This is the method by which mesh networks can cover larger distances than the range of any single device. For a network to operate, every node must be within range of at least one relay so that its messages can be forwarded on to nodes that it cannot directly communicate with.
It is common for all except low power nodes to implement a relay feature in order to maximize the possible paths through a mesh network.
Relay nodes keep a cache of messages to prevent messages from cycling.  Each mesh message also has a TTL (time to live) to prevent cycling.
GATT Proxy Node
Many existing BLE devices support traditional BLE GATT communication but not mesh communication. Most smartphones and tablets fall into this category. Since you may want to interact with a mesh network from one of those devices, the GATT proxy was created. A GATT proxy node has both a mesh interface and a GATT interface. The GATT interface is used to communicate with BLE devices that don’t possess a mesh stack and then relay those messages to/from the mesh network. That is, the GATT proxy acts as a bridge between the mesh network and the traditional BLE GATT device.
Friend and Low Power Nodes
Friend and Low Power Nodes are used to optimize power consumption for constrained devices. Devices that are power constrained (e.g. a battery powered device) are designated as low power nodes. Every low power node in the network must be associated with exactly one friend node. Friend nodes are devices which are not power constrained (e.g. a device plugged into AC power) that support 1 or more low power nodes depending on its capabilities (e.g. available RAM).
When a low power node is added to a mesh network it broadcasts a request for a friend. Each friend in range that can handle a new low power node replies and the low power node selects the best friend based on how many messages the friend can store; the RSSI and the timing accuracy.
Once the relationship is established, the friend node will receive and store messages for any low power nodes that it is associated with. The low power node will periodically ask the friend node for any messages that the friend has stored for it. In this way, the low power node does not need to listen continuously for mesh packets. Instead, it can be in a low power mode most of the time and can wake up only periodically for a very short time.
For example, consider a battery powered mesh connected thermostat. It will measure the actual temperature and may publish a mesh message with the temperature once per minute. This can be done with very low power consumption since the device can be sleeping all the time except for a short period each minute to send the value. However, it must also be possible to change the set point of the thermostat. In this case, instead of sending messages, the thermostat must be listening for messages. If it listens constantly for messages the power consumption will be unacceptably high, but if it only listens occasionally for messages it will likely miss messages. By making the thermostat a low power node we get the best of both worlds – it can send messages once a minute and receive any stored messages regarding the set point from its friend node. No messages are missed even though the thermostat is awake only a very small percentage of the time.

Bluetooth Mesh Elements

An Element is just a “Thing” in the network.  For instance a light bulb or a light switch or a temperature sensor.  A physical node can be built up of multiple elements.  Think of a ceiling fan that also has a light bulb.

Each Element in the network will have an address and be uniquely addressable.  This means that a Node may have multiple Bluetooth Mesh addresses, but it will have only one Bluetooth MAC address.

Bluetooth Mesh Addressing

Mesh messages have a source address and a destination address. Both addresses are 16-bit values. There are three types of addresses defined for messages. They are:
1. Unicast
2. Group
3. Virtual

Address Type Address Range Number of Addresses
Unassigned 0b0000000000000000 1
Unicast 0b0xxxxxxxxxxxxxxx 32767
Group 0b11xxxxxxxxxxxxxx 16384
Virtual 0b10xxxxxxxxxxxxxx 16384 hash values

Unicast
A unicast address is used to communicate with a single element in a single node. Each element in a network must have a unicast address that is unique to that network. During provisioning, the primary element in a node is assigned a unique unicast address and each additional element in the node uses the next address.
The source address in any message must be a unicast address. That is, the message must specify the specific element that message was sent by. Group and Virtual addresses are not allowed as the source address.

Group
As the name implies, a group address is used to communicate with one or more elements. Group addresses are either defined by the Bluetooth SIG (known as fixed group addresses) or are assigned dynamically for a given mesh network. There are 16K total group addresses available. The SIG has reserved space for 256 of them to be fixed while the rest can be dynamically chosen by the network.
Of the 256 group addresses that the SIG has reserved for fixed addresses, currently only 4 of them are assigned specific purposes. The rest are reserved for future use. They are:

Fixed Group Address Name
0b1111111100000000 – 0b1111111111111011 Reserved
0b1111111111111100 all-proxies
0b1111111111111101 all-friends
0b1111111111111110 all-relays
0b1111111111111111 all-nodes

Other group addresses can be assigned for any logical group in the network. For example, room names such as kitchen, bedroom or living room could be identified as group names to control multiple elements at once. As another example, you can have one switch turn on/off multiple bulbs at the same time with a single message to a group address.

Virtual
A virtual address is assigned to one or more elements across one or more nodes. A virtual address takes the form of a 128-bit UUID that any element can be assigned to, like a label. This 128-bit address is used in calculating the message integrity check.
The 14 LSBs of the virtual address are set to a hash of the label UUID such that each hash represents many label UUIDs. When an access message is received for a virtual address with a matching hash, each corresponding label UUID is compared as part of the authentication to see if there is a matching element.
This may be used by a manufacturer to allow mesh networks including those devices to send messages to all similar devices at one time.

Bluetooth Mesh Messaging

The Bluetooth Mesh communication happens using BLE Advertising packets. There are two classes of messages in the Bluetooth Mesh, Control and Access.  By and large the control messages are used for network control, and you never see them as they are handled by the stack.  The Access messages are ones that matter to us as application developers.

We all know that the BLE advertising  packet is only 31 bytes long.  This makes things difficult as most of the packet is used up by network protocol overhead leaving only a few bytes for the actual message.  The good news is that the stack handles splitting up your payload into as many as 32 packets (called segmented) and getting them re-sequenced automatically.

I’m not very good at limits … but this is what you have to live with in BLE Mesh:

Message Type Max Payload Size (Octets)
Unsegmented Control or Access 11
Segmented Control 256
Segmented Access 376 or 380

Acknowledged vs. Unacknowledged
As the name suggests, acknowledged messages require a response from the node that it is addressed to. The response confirms that the message was received and it may also return data back to the sender (e.g. in response to a GET). If a sender does not receive the expected response from a message it may resend it. Messages must be idempotent so that a message received more than once is no different than if it had only been received once.

GET, SET, STATUS
All access messages are of the three broad types of GET, SET, and STATUS.
GET messages request a state value from one or more nodes. A GET message is always acknowledged.
SET messages are used to change the value of a state. A SET message can be either acknowledged or unacknowledged.
STATUS messages are sent in response to a GET, and acknowledged SET, or may also be sent by a node independently, for example, periodically using a timer. A STATUS message is always unacknowledged. Therefore, if a node sends a GET message but never receives a STATUS return message, it may resend the GET message.

BLE Mesh Publishing

From the BLE Mesh Spec “All communication within a mesh network is accomplished by sending messages. Messages operate on states. For each state, there is a defined set of messages that a server supports and a client may use to request a value of a state or to change a state. A server may also transmit unsolicited messages carrying information about states and/or changing states.”

The BLE Mesh Application communication scheme is based on the Publish/Subscribe & Client/Server paradigm and are embedded automatically into Access messages by the stack.  An Element may publish messages (either to a group or a unicast address).  And an Element may subscribe to messages from one or more groups.

Models

An Element is not very interesting without a mechanism to interact with it.  A Model is exactly that, specifically it is the Bluetooth SIG defined behavior and data/state of an Element.  Each Element in your Node will have one or more Models that are attached to it that you can think of as Servers which hold, send and receive data.

Models fall into three categories. Servers, Clients and Control (hybrid Server/Control)

Server:  Contains data and sends it out in response to Client GET requests or can update the data based on Client SET requests or may send Status based on changes in the Element.

Clients: Send GET requests to Servers or Send SET requests to Servers.

Control: A Hybrid Model that acts both as a Client and a Server.

Here is an example picture from the AN227069 – Getting Started with Bluetooth Mesh

To make this system work the Bluetooth SIG has also defined the standard behavior for a bunch of different models including:

States

From the Bluetooth Spec “A state is a value representing a condition of an element.”  States are associated with a particular server model. That is, the spec defines the states that apply to each server model and how they behave.

Complexity

There is quite a bit more going on in the Bluetooth Mesh specifications including the abilities to handle:

  • Scenes (e.g. go in a room and have all the lights, sound, hvac go to the right levels)
  • State binding – Multiple states are bound together such that when one changes the others change (e.g. you turn the volume so low that it becomes off)
  • State transition times (e.g. Fade the lights up or down over a set time period)
  • Alerts (e.g. notify the user with a blinking light)

Security

There are three levels of security in a Bluetooth Mesh network and access is governed by three keys.

NetKey
All nodes in a mesh network must possess the network key. In fact, possession of the NetKey is what makes a node a member of a given mesh network. The NetKey allows a node to decrypt and authenticate messages at the network Layer. The mesh packet header and address are encrypted and authenticated with the network key. This allows a node to perform relay functions, but it does NOT allow the relay node to decrypt the application data that is stored in a message.
AppKey
The mesh packet payload is encrypted and authenticated with the application key. Therefore, data for a specific application can only be decrypted by nodes that have the AppKey for that application. The AppKeys are used by the upper transport layer to decrypt and authenticate messages before passing them to the access layer.
The existence of AppKeys allows multiple applications to share a mesh network (and therefore gain all the benefits of having more nodes such as increased reliability and range) without each node having access to all messages.
For example, consider a mesh network that has lights, HVAC, and home security devices on it. The light fixtures and light switches would share an AppKey for lighting; the thermostats, furnace, and air conditioner would share an AppKey for HVAC; the door locks and alarm system would share an AppKey for home security. In this way, home security messages can only be decrypted by devices that are part of the home security system, etc.
DevKey
Each device has its own unique device key known only to itself and the provisioner device. This key is used for secure communication during configuration.

Provisioning and Configuration

When a node turns on for the first time it barely knows its own name.  It definitely does not know any of the security keys, its addresses (unicast or group) or any of its model configuration information.  So what does it do?  Simple – it starts to send out a BLE Advertising packet in the format of a BLE Mesh Beacon.  Then it waits for a provisioning device to make a BLE GATT connection to provision the node with the network information.  The provisioning process assigns the netkey and the unicast address of the primary  element.

After the device is provisioned it will be able to hear and decode BLE Mesh packets.  But, it won’t know much else to do until the Elements have been configured.  So, the next step for the provisioning application is to send the rest of the configuration information. e.g. group subscriptions, application keys etc.

Bluetooth Mesh Stack

Mouser Bluetooth Mesh: L4 Integrating the Modus Toolbox Code Examples

How To Design With Bluetooth Mesh


You can "git" a workspace will all of these files at https://github.com/iotexpert/MouserVTWBluetoothMesh or git@github.com:iotexpert/MouserVTWBluetoothMesh.git

Summary

In this lesson I will show you how to find and use code examples.  I’ll then provision a motion sensor into my Bluetooth Mesh network and test.  Here are the steps:

  1. How to Find the Code Examples
  2. How to Import the Code Examples
  3. Explore the Code Examples
  4. How to Use a Code Example
  5. Provision the Motion Sensor Into the Network
  6. Test the Motion Sensor

How to Find the Code Examples

In the ModusToolbox Quick Panel pick “Search Online for Code Examples”

You will find yourself in a web browser on the cypressemiconductorco GitHub site.

Click on the link “Bluetooth SDK Examples”

You can look around on the website at the examples.  Or …

How to Import the Code Examples

Click the “Clone or download” button.  This will give you this window which has the SSH or HTTPS address of the site.

Copy the location of the github repo “git@github.com:cypresssemiconductorco/Code-Examples-BT-SDK-for-ModusToolbox.git”.  Then go back to the Workspace Explorer.  Right click and select “Import…”

Choose Git–>Projects from Git

Select “Clone URI” (so you can get the examples from the Internet)

Paste in the URI (which you copied from the web browser) Then press Next–>

Pick out the master branch.

You will now need to tell Eclipse where you want it to save the repository on your local machine.  I let it select the default location and press Next –>

To make things easy, you can then import that directory full of goodness into your Workspace.  An excellent developer I know says that Code is all of the documentation you need.  Which means that it is nice to be able to browse the code in a good editor.

Explore the Code Examples

Now you should have the folder “Code-Examples-BT-20819A1-1.0-for-ModusToolbox” inside of your Workspace.

If you double click the “BT-SDK-1.2-Code-Examples.pdf” you will get a description of them all

You can walk through them using the Workspace Explorer.  Notice that we created examples for many of the Bluetooth Mesh Models.

Example Peer Apps

We had a problem with Peer Apps in the SDK1.2 release and this was fixed by adding the Peer Apps to the Example Projects.

How to use a Code Example

To create a project from one of the code examples, start by choosing “File->New->ModusToolbox IDE Application”

Pick the correct development kit “CYBT-213043-MESH”

Select “Import”

Navigate to the file “modus.mk” of the example you want.  This will be in the directory on your computer where you cloned the GitHub repository.

After doing the Import you will have that example as a Starter Application.  Notice that they have an asterisks beside the name.  Type the name of your application if you want to change it.  In this case I pick “VTW_Mesh_SensorMotion”.  Then press “Next->”

Click “Finish”

Now you will have the application in the Workspace.

You can now press “VTW_Mesh_SensorMotion Build + Program”

And you will end up with this in your console after sweet Success!!!

Provision the Motion Sensor into the Network

I now have a board programmed with the Motion Sensor Application.  Seems like that it will be a good idea to provision it into the network and see if it is doing anything.  Press “Scan Unprovisioned”.

Once I see my device, I’ll stop scanning and then press “Provision and configure”.  After a minute our tool will tell you that it is done!

Test the Motion Sensor

You can request the state of the sensor by clicking “Control” and then picking the motion sensor.  Now press the “Get” button for the sensor and you will see that the sensor is graced with my presence.

Each time I click “Get” you can see the transaction happening on the development kit.

Mouser Bluetooth Mesh: L3 Making a Light Switch Using the Example Code

How To Design With Bluetooth Mesh


You can "git" a workspace will all of these files at https://github.com/iotexpert/MouserVTWBluetoothMesh or git@github.com:iotexpert/MouserVTWBluetoothMesh.git

Summary

In the first several lessons I showed you how to build a BLE Mesh Network using the Cypress Android App and the Windows Application.  I don’t know about you, but even though I live twenty years back in time in Kentucky I still have light switches.  In this lesson Ill show you how to add a light switch to your network.  And then we will have a good look a the code because we have run enough demo projects.

Given that I have a working network from the last lesson I will just add the switch to that network.  Meaning, I will use the Mesh Client to provision in the new switch.

For this exercise we will:

  1. Create the VTW_MeshOnOffSwitch Project
  2. Provision and Configure the Switch
  3. Firmware Architecture
  4. Examine the Code

Create the VTW_MeshOnOffSwitch Project

Start a new project with File–>New–>ModusToolbox IDE Application

Pick out the CYBT-213043-MESH hardware.

I will use the BLE_Mesh_OnOffSwitch starter application.  Notice that I named it “VTW_Mesh_OnOffSwitch”

Now press Finish to create the application in your workspace.

Now your Project Explorer should look like this.  Notice that in the quick panel there are the launches for this application.

So, press “VTW_MeshOnOffSwitch Build + Program”

Provision and Configure the Switch

Go back to the Mesh Client application.  Just as before you can add the Switch to the network by scanning for unprovisioned BLE Mesh Nodes.  When the node shows up, give it a name of SW1 and then press “Provision and configure”

After a while, your trace window will indicate success via the ever informative “done” message.

If you have a serial terminal attached to the development kit you will see what happens during the provision/configuration process.

Now test the button on your new Light Switch and notice that both LEDs turn on (both Dimmable and L2).  You can configure the Switch to control only L2 if you want.  On the “Use Device” drop down pick “SW1 (0004)” then select control “ONOFF” and pick the switch “L2”.

After applying the change you can see that switch only controls L2.  You can then switch it to “Dimmable” to control the other LED.

Firmware Architecture

Examine the Code

A Cypress Bluetooth Mesh project has several basic sections  Specifically you must configure:

  1. Elements & Models
  2. Mesh Config
  3. Application Function Pointers
  4. mesh_app_init

Plus for the switch application, you will need to configure the button management code (what happens when the button is pushed).

    Elements and Models

    The “Element” is a fundamental unit of thingness in a BLE Mesh.  For instance in the projects we have looked at, the LEDs are represented by an Element, and the switch is represented by another element.  Each element will have one or more models that represent the functionality of the element.  For instance, in a switch you will have a “WICED_BT_MESH_MODEL_ONOFF_CLIENT”.   If you look at on_off_switch.c you will see that we first define an array of models.  Then we define an array of elements.  The array mesh_element1_models will be associated with the the first element.

    wiced_bt_mesh_core_config_model_t   mesh_element1_models[] =
    {
        WICED_BT_MESH_DEVICE,
        WICED_BT_MESH_MODEL_ONOFF_CLIENT,
    };
    #define MESH_APP_NUM_MODELS  (sizeof(mesh_element1_models) / sizeof(wiced_bt_mesh_core_config_model_t))
    
    #define ONOFF_SWITCH_ELEMENT_INDEX   0
    
    wiced_bt_mesh_core_config_element_t mesh_elements[] =
    {
        {
            .location = MESH_ELEM_LOC_MAIN,                                 // location description as defined in the GATT Bluetooth Namespace Descriptors section of the Bluetooth SIG Assigned Numbers
            .default_transition_time = MESH_DEFAULT_TRANSITION_TIME_IN_MS,  // Default transition time for models of the element in milliseconds
            .onpowerup_state = WICED_BT_MESH_ON_POWER_UP_STATE_RESTORE,     // Default element behavior on power up
            .default_level = 0,                                             // Default value of the variable controlled on this element (for example power, lightness, temperature, hue...)
            .range_min = 1,                                                 // Minimum value of the variable controlled on this element (for example power, lightness, temperature, hue...)
            .range_max = 0xffff,                                            // Maximum value of the variable controlled on this element (for example power, lightness, temperature, hue...)
            .move_rollover = 0,                                             // If true when level gets to range_max during move operation, it switches to min, otherwise move stops.
            .properties_num = 0,                                            // Number of properties in the array models
            .properties = NULL,                                             // Array of properties in the element.
            .sensors_num = 0,                                               // Number of sensors in the sensor array
            .sensors = NULL,                                                // Array of sensors of that element
            .models_num = MESH_APP_NUM_MODELS,                              // Number of models in the array models
            .models = mesh_element1_models,                                 // Array of models located in that element. Model data is defined by structure wiced_bt_mesh_core_config_model_t
        },
    };
    

    Mesh Config

    The mesh config structure handles the basic setup for the Cypress Bluetooth Mesh.  This structure is loaded in by the stack when it starts.

    wiced_bt_mesh_core_config_t  mesh_config =
    {
        .company_id         = MESH_COMPANY_ID_CYPRESS,                  // Company identifier assigned by the Bluetooth SIG
        .product_id         = MESH_PID,                                 // Vendor-assigned product identifier
        .vendor_id          = MESH_VID,                                 // Vendor-assigned product version identifier
        .firmware_id        = MESH_FWID,                                // Vendor-assigned firmware version identifier
        .replay_cache_size  = MESH_CACHE_REPLAY_SIZE,                   // Number of replay protection entries, i.e. maximum number of mesh devices that can send application messages to this device.
    #if defined(LOW_POWER_NODE) && (LOW_POWER_NODE == 1)
        .features           = WICED_BT_MESH_CORE_FEATURE_BIT_LOW_POWER, // A bit field indicating the device features. In Low Power mode no Relay, no Proxy and no Friend
        .friend_cfg         =                                           // Empty Configuration of the Friend Feature
        {
            .receive_window = 0,                                        // Receive Window value in milliseconds supported by the Friend node.
            .cache_buf_len  = 0,                                        // Length of the buffer for the cache
            .max_lpn_num    = 0                                         // Max number of Low Power Nodes with established friendship. Must be > 0 if Friend feature is supported. 
        },
        .low_power          =                                           // Configuration of the Low Power Feature
        {
            .rssi_factor           = 2,                                 // contribution of the RSSI measured by the Friend node used in Friend Offer Delay calculations.
            .receive_window_factor = 2,                                 // contribution of the supported Receive Window used in Friend Offer Delay calculations.
            .min_cache_size_log    = 3,                                 // minimum number of messages that the Friend node can store in its Friend Cache.
            .receive_delay         = 100,                               // Receive delay in 1ms units to be requested by the Low Power node.
            .poll_timeout          = 36000                              // Poll timeout in 100ms units to be requested by the Low Power node.
        },
    #else
        .features           = 0,                                        // no, support for proxy, friend, or relay
        .friend_cfg         =                                           // Empty Configuration of the Friend Feature
        {
            .receive_window        = 0,                                 // Receive Window value in milliseconds supported by the Friend node.
            .cache_buf_len         = 0,                                 // Length of the buffer for the cache
            .max_lpn_num           = 0                                  // Max number of Low Power Nodes with established friendship. Must be > 0 if Friend feature is supported. 
        },
        .low_power          =                                           // Configuration of the Low Power Feature
        {
            .rssi_factor           = 0,                                 // contribution of the RSSI measured by the Friend node used in Friend Offer Delay calculations.
            .receive_window_factor = 0,                                 // contribution of the supported Receive Window used in Friend Offer Delay calculations.
            .min_cache_size_log    = 0,                                 // minimum number of messages that the Friend node can store in its Friend Cache.
            .receive_delay         = 0,                                 // Receive delay in 1 ms units to be requested by the Low Power node.
            .poll_timeout          = 0                                  // Poll timeout in 100ms units to be requested by the Low Power node.
        },
    #endif
        .gatt_client_only          = WICED_FALSE,                       // Can connect to mesh over GATT or ADV
        .elements_num  = (uint8_t)(sizeof(mesh_elements) / sizeof(mesh_elements[0])),   // number of elements on this device
        .elements      = mesh_elements                                  // Array of elements for this device
    };
    

    Application Function Pointers

    The Bluetooth Mesh stack interacts with your program via functions that it runs when something is happening.  You can either accept the default behavior, or you can write your own function.

    /*
     * Mesh application library will call into application functions if provided by the application.
     */
    wiced_bt_mesh_app_func_table_t wiced_bt_mesh_app_func_table =
    {
        mesh_app_init,          // application initialization
        mesh_app_hardware_init, // hardware initialization
        NULL,                   // GATT connection status
        NULL,                   // attention processing
        NULL,                   // notify period set
        NULL,                   // WICED HCI command
        NULL,                   // LPN sleep
        NULL                    // factory reset
    };

    mesh_app_init

    The function mesh_app_init is called by the stack when the stack starts.  It is your opportunity to get things going.   In the “if” we setup information which makes it easier to see what is happening when you provision by adding that information to the scan response packet.

    The last thing we do in mesh_app_init function is initialize the onoff client model. This should be done for any models that your device uses. The model initialization functions can accept the name of a function that we want the stack to call when it receives messages from the network for that model. In this case, we don’t need to do anything in our firmware for onoff client messages, so we specify NULL.

    /******************************************************
     *               Function Definitions
     ******************************************************/
    void mesh_app_init(wiced_bool_t is_provisioned)
    {
    #if 0
        extern uint8_t wiced_bt_mesh_model_trace_enabled;
        wiced_bt_mesh_model_trace_enabled = WICED_TRUE;
    #endif
        wiced_bt_cfg_settings.device_name = (uint8_t *)"Switch";
        wiced_bt_cfg_settings.gatt_cfg.appearance = APPEARANCE_CONTROL_DEVICE_SLIDER;
    
    #if defined(LOW_POWER_NODE) && (LOW_POWER_NODE == 1)
        WICED_BT_TRACE("LPN Switch init provisioned:%d\n", is_provisioned);
    #else
        WICED_BT_TRACE("Switch init provisioned:%d\n", is_provisioned);
    #endif
    
        // Adv Data is fixed. Spec allows to put URI, Name, Appearance and Tx Power in the Scan Response Data.
        if (!is_provisioned)
        {
            wiced_bt_ble_advert_elem_t  adv_elem[3];
            uint8_t                     buf[2];
            uint8_t                     num_elem = 0;
    
            adv_elem[num_elem].advert_type = BTM_BLE_ADVERT_TYPE_NAME_COMPLETE;
            adv_elem[num_elem].len         = (uint16_t)strlen((const char*)wiced_bt_cfg_settings.device_name);
            adv_elem[num_elem].p_data      = wiced_bt_cfg_settings.device_name;
            num_elem++;
    
            adv_elem[num_elem].advert_type = BTM_BLE_ADVERT_TYPE_APPEARANCE;
            adv_elem[num_elem].len         = 2;
            buf[0]                         = (uint8_t)wiced_bt_cfg_settings.gatt_cfg.appearance;
            buf[1]                         = (uint8_t)(wiced_bt_cfg_settings.gatt_cfg.appearance >> 8);
            adv_elem[num_elem].p_data      = buf;
            num_elem++;
    
            wiced_bt_mesh_set_raw_scan_response_data(num_elem, adv_elem);
        }
    
        // This application does not check result of the transmission or status event from the
        // target device.  Initialize OnOff client library not registering the callback.
        wiced_bt_mesh_model_onoff_client_init(ONOFF_SWITCH_ELEMENT_INDEX, NULL, is_provisioned);
    }
    

    Button Management Code

    Now you just need to setup the button hardware in the mesh_app_hardware_init function to generate an interrupt when the button is pressed or released..

    void mesh_app_hardware_init(void)
    {
        /* Configure buttons available on the platform */
    #if defined(CYW20706A2)
        wiced_hal_gpio_configure_pin(WICED_GPIO_BUTTON, WICED_GPIO_BUTTON_SETTINGS(GPIO_EN_INT_BOTH_EDGE), WICED_GPIO_BUTTON_DEFAULT_STATE);
        wiced_hal_gpio_register_pin_for_interrupt(WICED_GPIO_BUTTON, button_interrupt_handler, NULL);
    #elif (defined(CYW20735B0) || defined(CYW20719B0) || defined(CYW20721B0))
        wiced_hal_gpio_register_pin_for_interrupt(WICED_GPIO_PIN_BUTTON, button_interrupt_handler, NULL);
        wiced_hal_gpio_configure_pin(WICED_GPIO_PIN_BUTTON, WICED_GPIO_BUTTON_SETTINGS, GPIO_PIN_OUTPUT_LOW);
    #else
        wiced_platform_register_button_callback(WICED_PLATFORM_BUTTON_1, button_interrupt_handler, NULL, GPIO_EN_INT_BOTH_EDGE);
        button_previous_value = platform_button[WICED_PLATFORM_BUTTON_1].default_state;
    #endif
    }

    The interrupt handler will call the function wiced_bt_mesh_model_onoff_client_set to publish the button pressed message.

    /*
     * Process interrupts from the button.
     */
    void button_interrupt_handler(void* user_data, uint8_t pin)
    {
        uint32_t value = wiced_hal_gpio_get_pin_input_status(pin);
        uint32_t current_time = wiced_bt_mesh_core_get_tick_count();
        uint32_t button_pushed_duration;
    
        if (value == button_previous_value)
        {
            WICED_BT_TRACE("interrupt_handler: duplicate pin:%d value:%d current_time:%d\n", pin, value, current_time);
            return;
        }
        button_previous_value = value;
    
        WICED_BT_TRACE("interrupt_handler: pin:%d value:%d current_time:%d\n", pin, value, current_time);
    
        if (value == platform_button[WICED_PLATFORM_BUTTON_1].button_pressed_value)
        {
            button_pushed_time = current_time;
            return;
        }
        // button is released
        button_pushed_duration = current_time - button_pushed_time;
        if (button_pushed_duration < 15000)
        {
            process_button_push(ONOFF_SWITCH_ELEMENT_INDEX);
        }
        else
        {
            // More than 15 seconds means factory reset
            mesh_application_factory_reset();
        }
    }
    
    void process_button_push(uint8_t element_idx)
    {
        static uint8_t onoff = 0;
        wiced_bt_mesh_onoff_set_data_t set_data;
    
        onoff ^= 1;
    
        set_data.onoff           = onoff;
        set_data.transition_time = WICED_BT_MESH_TRANSITION_TIME_DEFAULT;
        set_data.delay           = 0;
    
        wiced_bt_mesh_model_onoff_client_set(element_idx, &set_data);
    }
    

     

    Mouser Bluetooth Mesh: L2B Building a Mesh Network Using the Mesh Client

    How To Design With Bluetooth Mesh


    You can "git" a workspace will all of these files at https://github.com/iotexpert/MouserVTWBluetoothMesh or git@github.com:iotexpert/MouserVTWBluetoothMesh.git

    Summary

    In this lesson I will show you how to use the Windows Application called “Mesh Client” to control your Bluetooth Mesh network.  The Mesh Client program attaches to the Bluetooth adaptor in your computer and lets you control Bluetooth Mesh networks (genius of a marketing name).  This includes provisioning, configuring, operation, etc.  This lesson will mirror the previous one in that I will setup two dimmable lights but this time I will control them via PC based software instead of the Android app.

    For this exercise I will take you through the following steps:

    1. Reset the Two Kits by Reprogramming
    2. Run the Mesh Client
    3. Create a New Bluetooth Mesh Network
    4. Provision your L1 Dimmable Light to the Network
    5. Control the L1 Dimmable Light
    6. Add the other Dimmable Light to the Network & Test

    Reset the Two Kits by Reprogramming

    Click on the “VTW_Mesh_LightDimmable” project.  Then select “VTW_Mesh_LightDimmable Build + Program”.  Do this to both of your boards so that we can start fresh.

    Run the Mesh Client

    The Mesh Client application can be found in your ModusToolbox installation directory.  Here is the path:

    Edit: There is a bug in this release.  You need to get the MeshClient from the code example … Lesson 4

    When you run the Mesh Client it will look like this:

    Create a New Network

    Before it will do anything, you need to create a new network by giving it a name such as “Net2” and pressing “Create”.  This will populate all of the configuration files required to control the network.

    After the network is created, press the “Open” button.

    After the network configuration files load you will see a trace window like this:

    Provision your L1 Dimmable Light to the Network

    Now you have a network with no nodes.  Press “Scan Unprovisioned” to find an unprovisioned device, which are just BLE devices that are advertising as mesh beacons.

    After a bit, the scanner will find your “Dimmable Light”. You will see the device show up in “Provision UUID” with a name.   You can click Stop Scanning so that it doesn’t keep searching for other devices. Right here, I would recommend you change the device name.  Unfortunately I forgot when I captured these screens.  Oh well.  I hope when I do this live I won’t forget.  Now press “Provision and configure”.  After a some time that process will complete.

    While the provision and configuration process is going on you will see the console of the Dimmable light reacting.

    Control the L1 Dimmable Light

    Now that you have a provisioned node you can use the Mesh Client to control it.  Start by selecting which Node to control in the drop down dialog box.  In this case I chose “Dimmable light” which makes sense as it is the only device in my network.

    Now select “On” and “Set” which will turn on the LED.

    Add the other Dimmable Light to the Network & Test

    Unplug the Dimmable Light and plug it into something other than your computer.  Plug in the other unprovisioned board.  Back in the Mesh Client application press “Scan Unprovisioned”  When it comes back having found the new board,  give it a name “L2” and then press “Provision and configure”.  When I do this live during the workshop I will name the first board “L1” and the second “L2”.  But I’m not going to recapture these screen shots.

    Once L2 is provisioned into the network.  I can control it the same way I did with “L1”.  Select “L2” (and notice that I called the first light “Dimmable Light” rather than giving it a sensible name.

    Now select “On” and then press “Set”

    Finally, test and make sure that the first Dimmable Light is still working by turning it off:

    Mouser Bluetooth Mesh: L2A Using the CYBT-213043-MESH & Building a Mesh Network Using the Mesh Lighting App

    How To Design With Bluetooth Mesh


    You can "git" a workspace will all of these files at https://github.com/iotexpert/MouserVTWBluetoothMesh or git@github.com:iotexpert/MouserVTWBluetoothMesh.git

    Summary

    In this lesson I will walk you through the process of creating your first Bluetooth Mesh Network.  It will be a simple network with two Dimmable Light nodes which we will control using an Android Application called Mesh Lighting.

    In this exercise I will:

    1. Show you the CYBT-213043-MESH development kit
    2. Create a Dimmable Light Bulb Project and Program (two boards)
    3. Create a Mesh Network using MeshLighting Android Application
    4. Add another Dimmable Light to the Mesh
    5. Create Groups (a.k.a. Rooms)

    CYBT-213043-MESH

    Here is a picture of one of the development boards.  It has

    • A CYBT-213043-02 module with a PCB antenna.  The module contains a CYW20819
    • A two channel UART to USB device used for programming and debug printing
    • Three buttons (Reset, Recover and User)
    • An Ambient Light Sensor
    • An RGB LED
    • A thermistor
    • A PIR motion sensor

    Create a Dimmable Light Bulb Project and Program (two boards)

    Start this process by programming two of the boards with the Dimmable Light firmware.  To create this firmware start with File–>New–>Modustoolbox IDE Application

    Choose the CY214043-MESH development kit

    Then pick “BLE_Mesh_LightDimmable” and give it a name “VTW_Mesh_LightDimmable”

    Now press finish to make the project.

    Modus Toolbox provides a Launches window on a “quick panel” right below the workspace explorer.  Pick “VTW_Mesh_LightDimmable Build + Program”

    The compiler will run and make an executable.  Then it will program the flash of the CYW20819.

    On a Serial Console you should see the Application Bootup (notice that the baud rate is 921600). Remember there are 2 UART channels on each kit – one primarily used for programming and one used for debug messages. Make sure to connect to the second channel called the peripheral UART or PUART.

    Create a Mesh Network using MeshLight Android Application

    We provide the Android Application called “WICED Mesh Lighting App” and all of the source code as part of the SDK distribution in Modus Toolbox.  You can find it here:

    Start the process on your Android phone by running the “WICED Mesh Lighting App”

    Once the App starts, your screen should look like this:

    Press the three dots icon and select “Create Network”

    Give it a name.  In this case I chose “test1”

    Click on “ALL”

    Then press “Add Device”.  Your screen will search for an Unprovisioned Mesh Device.  When it finds one it will bring up the Name of the device along with the UUID.  I give the device the name “l1” and I attach that name to the “Dimmable Light….”.  After you get that done press OK.

    After you press OK the App will provision and configure your Dimmable Light.  After that is done, your screen will look like this.

    The green bar along the bottom of the screen indicates that your phone is connected to the network – if it is red, you can click on “Connect to Network”. You can now control the LED using the little switch icon on the right.  In this picture I have the L1 turned on.

    Add another Dimmable Light to the Mesh

    One Dimmable Light, this is not much of a mesh.  To fix that I program another development kit.  Then I click on all and “Add Device”  After a bit you can add the next kit to your network.  Notice I call this one “l2”

    After the provisioning and configuring your screen will look like this.  Once again you can turn the lights on individually or as a group.

    Create Groups (A.K.A. Rooms)

    It is common for the home automation system to have “Rooms”.  In the world of Bluetooth Mesh these are called “Groups”  To create a group start on the main screen and click the “+” to create a new room

    On the New Room screen click the pencil icon.

    Then type the name of the room.  In this case “r1”

    Now do the same thing to make “r2”

    Back in the “All Screen” you can click on the individual Dimmable Lights to move them around.  Start by moving l1 by clicking on it.

    Click on the “Move to Group” button.

    And assign l1 to r1

    Go back.  Then click on l2.

    And move it to “r2”

    Now you have this.  Three groups.  ALL, r1, and r2.  You can turn everything in those groups on by pressing the switch.  In the screen capture below I turned on “r2”

    One more interesting thing to do is to add l1 and l2 to the All group.

    Start on the main screen and click “r1”.

    Then click “l1”.

    Press “Add to other Group” and select “ALL”

    Go back and press on “r2”, select “l2” and then press “l2” and “Add to other Group”.

    Now you can control the “All” group, “r1” or “r2”.

     

    Mouser Bluetooth Mesh: L0 Introduction

    Summary

    Register for my Bluetooth Mesh Virtual Workshop on May, 29 at 11:00AM Eastern Time!!

    Block Diagram - Cypress Semiconductor CYBT-213043-MESH Bluetooth Evaluation Kit

    Hello everyone.  This is lesson 0 of a series of 10 lessons about creating Bluetooth Mesh applications for the Cypress EZ Bluetooth Mesh Evaluation Kit CYBT-213043-MESH.  This class is called “How to Design with Bluetooth Mesh” because that is exactly what we are going to do – make some applications.  No powerpoint in sight.

    I am going to start by showing you the development kit and demonstrating how to use it.  It is actually 4 development kits, which makes sense because you need multiple boards to make an actual Bluetooth Mesh.  I will show you and Android App called the “Mesh Lighting” app which I will use to provision, configure and control the mesh.  My plan is to walk you through a bunch of learning resources about Bluetooth Mesh and teach you the key concepts.  Finally, I’ll show you some code and teach you how to write your own projects.

    I will attempt to go slowly enough for you to follow along, but if I go too fast, don’t worry you should be able to follow along with the instructions on this website.

    Todays virtual workshop has this agenda table which will show also show up on every page.  The links will work to take you through the different lessons.

    How To Design With Bluetooth Mesh


    You can "git" a workspace will all of these files at https://github.com/iotexpert/MouserVTWBluetoothMesh or git@github.com:iotexpert/MouserVTWBluetoothMesh.git

    You will need a few things for this class:

    CYBT-213043-MESH

    This “development kit” is actually four identical boards (and cables) which will let you build a complete Bluetooth Mesh.  You can purchase the kit from Mouser.

    Modus Toolbox 1.1

    Modus Toolbox 1.1 is our Eclipse based IDE for developing Bluetooth projects.  You can run this on Windows, Mac and Linux.

    Bluetooth Mesh SDK 1.2

    Cypress has been working continuously to keep our Bluetooth Mesh SDK up to date with changes in the Bluetooth SIG specifications for Mesh.  Our latest release as of today is Bluetooth SDK 1.2 which you will need to have installed.