Archive for the ‘Sensor’ Category

Configure Nordic Softdevice to Support Custom BLE Profile with Multiple Services and Characteristics

August 8, 2016

My recent BLE project involves developing a custom peripheral profile for a 9 DOF sensor fusion embedded system with multiple sensors, such as accelerometer, gyroscope, magnetometer, and altimeter, etc. The system employs a nRF52 BLE module to transfer raw sensor data, plus calculated sensor fusion data, to a BLE central device, namely an iOS/Android mobile device, or a desktop computer with BLE capability, such as OSX or Windows 10 PC.

Softdevice is Nordic’s BLE stack implementation. The nRF52 softdevice is named s132. The project uses the recent nRF5 SDK version 11.0.0 released in March 2016 for development.
This version of SDK comes with version 2.0.0 softdevice s132.

By default, s132 supports one custom service and reserves memory for some characteristics. When you dig into the softdevice’s header files, you can find that reserved memory size is 0x580. My experiments tell me that amount of memory can support over one dozen of characteristics. That’s good in many situations, but not enough for my project, which requires 3 custom services and 42 characteristics, including many with notification.

Fortunately the softdevice is architectured in such way that it can be configured in runtime to meet application developer’s need.
There are couple of example projects in nRF5 SDK use multiple custom services, but there is none about expanding the reserved memory to support many more characteristics.
Hopefully my experience mentioned here is of useful for people needs more characteristics, and save some of their time to avoid troubles I had.

The screenshot below shows all services and characteristics of the custom BLE profile designed in BDS. BDS (Bluetooth Developer Studio) is Bluetooth SIG‘s design, development and testing tool. Many vendors, including Nordic and TI, provide BDS plugins. A vendor’s plugin can be used to generate certain code for its platform, saving tremendous amount of time for developers. For example, with this shown design, Nordic version 1.2.4 plugin generates 6,393 lines of code in multiple source files. Imagine how long it may take coding all of this manually. I will cover BDS in more details later in a separate blog.

em-sfm_bds

 

Here’re the steps I used to configure the softdevice to meet the project requirement.

  1. Configure the count of custom services
    According to Nordic programming convention, softdevice configuration is done in the following function:

        static void ble_stack_init(void)                                    
    

    To specify the number of supported custom services is easy. The following code snippet sets up 3 services:

        ble_enable_params_t ble_enable_params;
        ble_enable_params.common_enable_params.vs_uuid_count = 3;           
    

     

  2. Configure the attribute table size
    Set up attribute table size is trickier, and I don’t think Nordic provided any such example in nRF5 SDK.
    In my instance, I added 0x700 more bytes of memory to support 42 characteristics. The reason is the default memory size of 0x580 supports 17 characteristics, and it supports 9 more characteristics after the size is adjusted to 0x800.

        /* 0x580 added by 0x700 */
        ble_enable_params.gatts_enable_params.attr_tab_size = 0x0c80;        
    

    Here’s the complete function:

    /**@brief Function for the SoftDevice initialization.
     *
     * @details It initializes the SoftDevice and the BLE event interrupt.
     */
    static void ble_stack_init(void)
    {
        uint32_t err_code;
        
        nrf_clock_lf_cfg_t clock_lf_cfg = NRF_CLOCK_LFCLKSRC;
        
        // Initialize SoftDevice.
        SOFTDEVICE_HANDLER_INIT(&clock_lf_cfg, NULL);
        
        ble_enable_params_t ble_enable_params;
        err_code = softdevice_enable_get_default_config(CENTRAL_LINK_COUNT,
                                                        PERIPHERAL_LINK_COUNT,
                                                        &ble_enable_params);
        APP_ERROR_CHECK(err_code);
        
        /*
         * Supports 3 custom services
         */
        ble_enable_params.common_enable_params.vs_uuid_count = 3;
    
        /* 0x580 added by 0x700 */
        ble_enable_params.gatts_enable_params.attr_tab_size = 0x0c80;
    
        //Check the ram settings against the used number of links
        CHECK_RAM_START_ADDR(CENTRAL_LINK_COUNT,PERIPHERAL_LINK_COUNT);
        // Enable BLE stack.
        err_code = softdevice_enable(&ble_enable_params);
        APP_ERROR_CHECK(err_code);
        
        // Subscribe for BLE events.
        err_code = softdevice_ble_evt_handler_set(ble_evt_dispatch);
        APP_ERROR_CHECK(err_code);
    }
    

     

  3. Configure the user application memory location and size
    The last key step is to update the linker script to adjust the offset and size of the memory available to the BLE peripheral application.
    I’m using GNU/Eclipse toolchain, the idea is the same when MDK or IAR is used.
    Here’s the default MEMORY section’s RAM attribute values of the linker script:

      RAM (rwx) :  ORIGIN = 0x20002080, LENGTH = 0xdf80               
    

    Here’s the modified values. Basically it moves forward the memory offset by the memory amount added (0x700), and reduce the available memory size by the same amount to make sure there is no overlap between softdevice and application memory regions.

      RAM (rwx) :  ORIGIN = 0x20002780, LENGTH = 0xd880                
    

    Note the up end of the RAM region is 0x20010000 (0x20002080+0xdf80, or 0x20002780+0xd880). Consider RAM origin starts at 0x20000000 for the softdevice, 0x10000 (64KB) is exactly the size of nRF52 RAM.

    Here’s the complete linker script:

    /* Linker script to configure memory regions. */
    
    SEARCH_DIR(.)
    GROUP(-lgcc -lc -lnosys)
    
    MEMORY
    {
      FLASH (rx) : ORIGIN = 0x1c000, LENGTH = 0x64000
      /* 0xc80 = 0x580 + 0x700 */
      RAM (rwx) :  ORIGIN = 0x20002780, LENGTH = 0xd880                
    }
    
    SECTIONS
    {
      .fs_data :
      {
        PROVIDE(__start_fs_data = .);
        KEEP(*(.fs_data))
        PROVIDE(__stop_fs_data = .);
      } > RAM
    } INSERT AFTER .data;
    
    INCLUDE "nrf5x_common.ld"
    

Nordic just released s132 softdevice v3.0.0 in August 2016, I will migrate soon and find out more details about the release.

Using Motion Detection To Wake Up Embedded System – A Practical Example

February 2, 2016

Put the embedded system into sleep while not in use is a must have for any battery powered applications.The tricky part is how to wake up the system if it has no peripherals, such as switch, to interact with. Using motion detection to wake up the system is a natural choice.
The example demonstrated here uses a digital accelerometer via I2C interface for motion detection. The accelerometer is configured to generate interrupts after motion is detected. The motion interrupt then wakes up the system from the deep sleep mode.
The following hardware, software and system configurations are used in the project. The custom KL03Z board is designed by Embedded Masters, other parts not used in the demo are not mentioned here. EFM32 Giant Gecko board and Simplicity Studio is optional and only used for Power/Energy Profiler.

Hardware Description
Freescale Kinetis Custom Board KL03Z MCU (Cortex-M0+ 48MHz, 32KB flash, 2 KB SRAM)
MMA8652 3-Axis 12-bit Digital Accelerometer
RGB LEDs
Silicon Labs EFM32 Giant Gecko (STK3700) Run Power/Energy Profiler
Segger JLink Debugger Program and/or Debug Kinetics MCU based hardware
Software Description
Freescale KDS 3.0 Freescale Kinetis MCU Eclipse IDE
Freescale KSDK 1.3 Kinetis SDK
Silicon Labs Simplicity Studio v3 Run Power/Energy Profiler
System Configuration Description
LPO Internal low power oscillator(1000Hz) works in very low power/leakage modes
LPTMR Low power timer with prescaler 3 to accommodate timer period up to 17.6 minutes
Clocking by LPO
I2C Baud rate 100Hz
MMA8652 Configured in Free-fall/Motion detection mode
Enable motion detection interrupt 2
Route motion detection interrupt 2 pin to PTB0 per KL03Z datasheet
PMC (Power Management Controller) VLPR (Very Low Power Run) mode while the system is up
VLPS (Very Low Power Stop) mode while in deep sleep
LLWU (Low Leakage Wakeup Unit) Route LLWU wake-up source pin P4 to MMA interrupt 2 pin
RGB LEDs Active-low

The key is to configure MMA8652 motion detection interrupt 2 and LLWU P4 to share the same pin such that the motion detection becomes the wake-up source. The following picture shows the KL03Z data sheet about how pins are assigned.

Pin Assignment

This is the application code blinks red and green LEDs 5 times before entering the sleep mode, and blinks blue LED once after waking up. Wakeup source is a 5 second timer and/or motion detection.

Application Code
/*!
 *  @brief Enter deep sleep mode, and use timer/motion detection to wakeup
 */
void enter_sleep()
{
    /*  Step 0 - Init MMA */
    mma8652_init( &i2c_devices, 0 );
    /* Step 1 - enable LPO timer to make it another wake up source (5s) */
    timer_set_period( 5*1000, true );

    /* Step 2 - Enable motion detection interrupt */
    enable_motion_interrupt( true );

    /* Step 3 - Switch to deep sleep mode VLPS */
    /* Using motion detection/timer as the wakeup sources */
    pmc_sleep( true, true );

    /* Step 4 - Exiting sleep mode */
    /* timer is reenabled by pmc_sleep call */
    /* disable motion detection interrupt */
    enable_motion_interrupt( false );
}

/*!
* @brief Main routine
*/
void main (void)
{
    hardware_init();
    /* initialize peripherals */
    peripheral_init();

    while(1)
    {
        for(int i=0; i<5; i++ )
        {
            LED1_ON;
            LED2_ON;
            delay100ms(1);
            LED2_OFF;
            delay100ms(1);
            LED1_OFF;
            delay100ms(1);
        }

        enter_sleep();
        LED3_ON;
        delay100ms(2);
        LED3_OFF;
        delay100ms(5);
    }
}

The following screenshot is taken from Simplicity Studio/Energy Profiler running session. It clearly indicates the system behavior and power consumption.
Power Profiler

This video clip shows the complete running, including power profiling process.

After much efforts, include working with Freescale FAE and applying some tricks listed in chip’s errata, the system consumes about 10uA while in sleep in VLPS mode.