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);
}

 

Recommended Posts

No comment yet, add your voice below!


Add a Comment

Your email address will not be published. Required fields are marked *