2018年2月24日 星期六

nRF52840 in SDK14: a simple customized BLE service example

From nordic SDK11 to SDK 14, there many changes to implement a customized BLE service.
this article present a simple customized BLE service, with a characteristic that you can read, write , and notify.

example function description:
press button 3 to turn on LED_2, and press button 4 to turn off LED_2, recent status of LED_2 can be notify. Write "1" on the same characteristic can turn on LED_2, and write "0" can turn it off .

This is adapt from ble_app_template in SDK14
locate in :
 ...\BLE\nRF5_SDK_14.2.0_17b948a\nRF5_SDK_14.2.0_17b948a\examples\ble_peripheral\ble_app_template

I use nRF52840 PDK (pca10056)

STEP 0. add these two files near by main.c


ble_rls.c
/*
author: Agatha Kuan
20180223
ble remote-controled light service
*/

#include "sdk_common.h"
#include "ble_rls.h"
#include <string.h>
#include "ble_srv_common.h"

#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"

static void on_connect(ble_rls_t * p_rls, ble_evt_t const * p_ble_evt)
{
 p_rls->conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
}

static void on_disconnect(ble_rls_t * p_rls, ble_evt_t const * p_ble_evt)
{
 UNUSED_PARAMETER(p_ble_evt);
 p_rls->conn_handle = BLE_CONN_HANDLE_INVALID;
}

static void on_write(ble_rls_t * p_rls, ble_evt_t const * p_ble_evt)
{  
 ble_gatts_evt_write_t const * p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;
 
 if ((p_evt_write->handle == p_rls->status_char_handles.value_handle)
 && (p_evt_write->len == 1)
 && (p_rls->control_write_handler != NULL))
 {
  p_rls->control_write_handler(p_ble_evt->evt.gap_evt.conn_handle, p_rls, p_evt_write->data[0]);
 }
 
}

void ble_rls_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
{
 if ((p_context == NULL) || (p_ble_evt == NULL)) 
  return;
 
 ble_rls_t * p_rls = (ble_rls_t *)p_context;
 
 switch (p_ble_evt->header.evt_id)
 {
  case BLE_GAP_EVT_CONNECTED:
   on_connect(p_rls, p_ble_evt);
   break;

  case BLE_GAP_EVT_DISCONNECTED:
   on_disconnect(p_rls, p_ble_evt);
   break;

  case BLE_GATTS_EVT_WRITE:
   on_write(p_rls, p_ble_evt);
   break;

  default:
   // No implementation needed.
   break;
 }
}

uint32_t status_char_add(ble_rls_t * p_rls, const ble_rls_init_t * p_rls_init)
{
 uint32_t            err_code;
 ble_gatts_char_md_t char_md;
 ble_gatts_attr_md_t cccd_md;
 ble_gatts_attr_t    attr_char_value;
 ble_uuid_t          ble_uuid;
 ble_gatts_attr_md_t attr_md;
 
 uint8_t    encoded_report_ref[BLE_SRV_ENCODED_REPORT_REF_LEN];
 uint8_t    init_len;
 uint8_t    init_status = CONTROLED_LED_OFF;
 
 if (p_rls->is_notification_supported)
 {
  memset(&cccd_md, 0, sizeof(cccd_md));

  // According to BAS_SPEC_V10, the read operation on cccd should be possible without
  // authentication.
  BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.read_perm);
  cccd_md.write_perm = p_rls_init->status_char_attr_md.cccd_write_perm;
  cccd_md.vloc       = BLE_GATTS_VLOC_STACK;
 }
 
 memset(&char_md, 0, sizeof(char_md));

 char_md.char_props.read   = 1;
 char_md.char_props.notify = (p_rls->is_notification_supported) ? 1 : 0;
 char_md.char_props.write  = 1;
 char_md.p_char_user_desc  = NULL;
 char_md.p_char_pf         = NULL;
 char_md.p_user_desc_md    = NULL;
 char_md.p_cccd_md         = (p_rls->is_notification_supported) ? &cccd_md : NULL;
 char_md.p_sccd_md         = NULL;
 
 BLE_UUID_BLE_ASSIGN(ble_uuid, BLE_UUID_STATUS_CHAR);
 
 memset(&attr_md, 0, sizeof(attr_md));
 attr_md.read_perm  = p_rls_init->status_char_attr_md.read_perm;
 attr_md.write_perm = p_rls_init->status_char_attr_md.write_perm;
 attr_md.vloc       = BLE_GATTS_VLOC_STACK;
 attr_md.rd_auth    = 0;
 attr_md.wr_auth    = 0;
 attr_md.vlen       = 0;
 
 memset(&attr_char_value, 0, sizeof(attr_char_value));

 attr_char_value.p_uuid    = &ble_uuid;
 attr_char_value.p_attr_md = &attr_md;
 attr_char_value.init_len  = sizeof(uint8_t);
 attr_char_value.init_offs = 0;
 attr_char_value.max_len   = sizeof(uint8_t);
 attr_char_value.p_value   = &init_status;
 
 err_code = sd_ble_gatts_characteristic_add(p_rls->service_handle, &char_md,
              &attr_char_value,
              &p_rls->status_char_handles);
 if (err_code != NRF_SUCCESS) 
  return err_code;
 
 if (p_rls_init->p_report_ref != NULL)
 {
  // Add Report Reference descriptor
  BLE_UUID_BLE_ASSIGN(ble_uuid, BLE_UUID_REPORT_REF_DESCR);
  
  memset(&attr_md, 0, sizeof(attr_md));
  attr_md.read_perm = p_rls_init->status_report_read_perm;
  BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&attr_md.write_perm);
  
  attr_md.vloc    = BLE_GATTS_VLOC_STACK;
  attr_md.rd_auth = 0;
  attr_md.wr_auth = 0;
  attr_md.vlen    = 0;

  init_len = ble_srv_report_ref_encode(encoded_report_ref, p_rls_init->p_report_ref);
  
  memset(&attr_char_value, 0, sizeof(attr_char_value));

  attr_char_value.p_uuid    = &ble_uuid;
  attr_char_value.p_attr_md = &attr_md;
  attr_char_value.init_len  = init_len;
  attr_char_value.init_offs = 0;
  attr_char_value.max_len   = attr_char_value.init_len;
  attr_char_value.p_value   = encoded_report_ref;
  
  err_code = sd_ble_gatts_descriptor_add(p_rls->status_char_handles.value_handle,
              &attr_char_value,
              &p_rls->report_ref_handle);
  
  if (err_code != NRF_SUCCESS)  
   return err_code;
  
 }
 else 
  p_rls->report_ref_handle = BLE_GATT_HANDLE_INVALID;
 
  
 return NRF_SUCCESS;
}

uint32_t ble_rls_init(ble_rls_t * p_rls, const ble_rls_init_t * p_rls_init)
{
 if (p_rls == NULL || p_rls_init == NULL) 
  return NRF_ERROR_NULL;
 
 uint32_t   err_code;
 ble_uuid_t ble_uuid;
 
 p_rls->control_write_handler  = p_rls_init->control_write_handler;
 p_rls->conn_handle     = BLE_CONN_HANDLE_INVALID;
 p_rls->is_notification_supported = p_rls_init->support_notification;
 
 BLE_UUID_BLE_ASSIGN(ble_uuid, BLE_UUID_RLS_SERVICE);
 
 err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &ble_uuid, &p_rls->service_handle);
 
 if (err_code != NRF_SUCCESS)
  return err_code;
 
 err_code = status_char_add(p_rls, p_rls_init);
 VERIFY_SUCCESS(err_code);

 return NRF_SUCCESS;
}

uint32_t ble_rls_light_status_update(ble_rls_t* p_rls,uint8_t recent_status)
{
 if (p_rls == NULL)    
        return NRF_ERROR_NULL;
 
 uint32_t err_code = NRF_SUCCESS;
 ble_gatts_value_t gatts_value;
 
 if(recent_status != p_rls->light_status)
 {
  memset(&gatts_value, 0, sizeof(gatts_value));

        gatts_value.len     = sizeof(uint8_t);
        gatts_value.offset  = 0;
        gatts_value.p_value = &recent_status;
   
  err_code = sd_ble_gatts_value_set(p_rls->conn_handle,
      p_rls->status_char_handles.value_handle,
      &gatts_value);
 if (err_code == NRF_SUCCESS)
  p_rls->light_status = recent_status;
 else
   return err_code;
  
 if ((p_rls->conn_handle != BLE_CONN_HANDLE_INVALID) && p_rls->is_notification_supported)
 {
     ble_gatts_hvx_params_t hvx_params;

            memset(&hvx_params, 0, sizeof(hvx_params));

            hvx_params.handle = p_rls->status_char_handles.value_handle;
            hvx_params.type   = BLE_GATT_HVX_NOTIFICATION;
            hvx_params.offset = gatts_value.offset;
            hvx_params.p_len  = &gatts_value.len;
            hvx_params.p_data = gatts_value.p_value;

            err_code = sd_ble_gatts_hvx(p_rls->conn_handle, &hvx_params);
 }
 else
   err_code = NRF_ERROR_INVALID_STATE;
 }
 
 return err_code;
}


ble_rls.h
/*
author: Agatha Kuan
20180223
ble remote-controled light service
*/

#ifndef BLE_RLS_H_
#define BLE_RLS_H_

#include <stdint.h>
#include <stdbool.h>
#include "ble.h"
#include "ble_srv_common.h"
#include "nrf_sdh_ble.h"
#include "nrf_ble_gatt.h"

#ifdef __cplusplus
extern "C" {
#endif

#ifndef BLE_RLS_OBSERVER_PRIO
#define BLE_RLS_OBSERVER_PRIO  2
#endif

#define BLE_RLS_DEF(_name)         \
static ble_rls_t _name;           \
NRF_SDH_BLE_OBSERVER(_name ## _obs,     \
      BLE_RLS_OBSERVER_PRIO,  \
      ble_rls_on_ble_evt, &_name);
         

#define BLE_UUID_RLS_SERVICE  0x0330
#define BLE_UUID_STATUS_CHAR  0x0331   /*read,notify,write*/ 
 
#define CONTROLED_LED_ON   1
#define CONTROLED_LED_OFF   0

typedef enum
{
 BLE_RLS_EVT_NOTIFICATION_ENABLED,   
 BLE_RLS_EVT_NOTIFICATION_DISABLED   
} ble_rls_evt_type_t;

typedef struct
{
 ble_rls_evt_type_t evt_type;        /**< Type of event. */
} ble_rls_evt_t;

typedef struct ble_rls_s ble_rls_t;

typedef void (*ble_rls_write_handler_t) (uint16_t conn_handle, ble_rls_t * p_rls, uint8_t new_state);

typedef struct
{
 ble_rls_write_handler_t   control_write_handler;
 bool       support_notification; 
 ble_srv_report_ref_t*   p_report_ref;
 uint8_t       init_status;
 ble_srv_cccd_security_mode_t status_char_attr_md;
 ble_gap_conn_sec_mode_t   status_report_read_perm;
}ble_rls_init_t;

struct ble_rls_s
{
 uint16_t      service_handle;
 ble_gatts_char_handles_t  status_char_handles; 
 uint16_t                       report_ref_handle;
 uint16_t      conn_handle;
 bool       is_notification_supported;
 uint8_t       light_status;
 ble_rls_write_handler_t   control_write_handler;
};

uint32_t ble_rls_init(ble_rls_t* p_rls, const ble_rls_init_t* p_rls_init);

void ble_rls_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context);

uint32_t ble_rls_on_status_change(uint16_t conn_handle, ble_rls_t * p_rls, uint8_t recent_state);

uint32_t ble_rls_light_status_update(ble_rls_t* p_rls,uint8_t recent_status);

#ifdef __cplusplus
}
#endif

#endif



STEP 1. add in main.c

#include "ble_rls.h"
BLE_RLS_DEF(m_rls);


about line 346
static void remote_control_light_handler(uint16_t conn_handle, ble_rls_t * p_rls, uint8_t new_state)
{
 if (new_state)
 {
  NRF_LOG_INFO("light off\r"); 
  bsp_board_led_off(BSP_BOARD_LED_1);

  ble_rls_light_status_update(&m_rls,CONTROLED_LED_OFF);
 }
 else
 {
  NRF_LOG_INFO("light on\r"); 
  bsp_board_led_on(BSP_BOARD_LED_1);

  ble_rls_light_status_update(&m_rls,CONTROLED_LED_ON);
 }
} 

 

static void services_init(void)
{
 ret_code_t     err_code;
 ble_rls_init_t rls_init;
 
 memset(&rls_init, 0, sizeof(rls_init));
 rls_init.support_notification = true;
 rls_init.control_write_handler = remote_control_light_handler;
 
 BLE_GAP_CONN_SEC_MODE_SET_OPEN(&rls_init.status_char_attr_md.cccd_write_perm);
 BLE_GAP_CONN_SEC_MODE_SET_OPEN(&rls_init.status_char_attr_md.read_perm);

 BLE_GAP_CONN_SEC_MODE_SET_OPEN(&rls_init.status_char_attr_md.write_perm);
 
 BLE_GAP_CONN_SEC_MODE_SET_OPEN(&rls_init.status_report_read_perm);
 
 err_code = ble_rls_init(&m_rls, &rls_init);
 APP_ERROR_CHECK(err_code); 
}


in bsp_event_handler
static void bsp_event_handler(bsp_event_t event)
{
    ret_code_t err_code;

    switch (event)
    {
        case BSP_EVENT_SLEEP:
     NRF_LOG_INFO("bsp_event_handler sleep_mode_enter \r\n");
            sleep_mode_enter();
            break; // BSP_EVENT_SLEEP

        case BSP_EVENT_DISCONNECT:
            err_code = sd_ble_gap_disconnect(m_conn_handle,
                                             BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
            if (err_code != NRF_ERROR_INVALID_STATE)
            {
                APP_ERROR_CHECK(err_code);
            }
            break; // BSP_EVENT_DISCONNECT

        case BSP_EVENT_WHITELIST_OFF:
            if (m_conn_handle == BLE_CONN_HANDLE_INVALID)
            {
                err_code = ble_advertising_restart_without_whitelist(&m_advertising);
                if (err_code != NRF_ERROR_INVALID_STATE)
                {
                    APP_ERROR_CHECK(err_code);
                }
            }
            break; // BSP_EVENT_KEY_0
      
       case BSP_EVENT_WAKEUP:
           NRF_LOG_INFO("bsp_event_handler sleep_mode WAKE UP \r\n");
           break;
    
       case BSP_EVENT_KEY_2: 
           NRF_LOG_INFO("light on\r"); 
           bsp_board_led_on(BSP_BOARD_LED_1);
  
           ble_rls_light_status_update(&m_rls,CONTROLED_LED_ON);
           break;
    
      case BSP_EVENT_KEY_3:
           NRF_LOG_INFO("light off\r");
           bsp_board_led_off(BSP_BOARD_LED_1); 
  
           ble_rls_light_status_update(&m_rls,CONTROLED_LED_OFF);
           break;

        default:
            break;
    }
}


in main function

// Start execution.
    NRF_LOG_INFO("Template example started.");
    application_timers_start();

    advertising_start(erase_bonds);
    bsp_board_led_on(BSP_BOARD_LED_2);


STEP.2 program and flash into your device

you can
press button 3 to turn on LED_2
press button 4 to turn off LED_2


on mobile phone, you can see

write 00 can turn off LED_2
write 01 can turn on it.


Note:
1.the example in this article is not the same as ble_app_blinky

2. the format and API I used are more similar to other common ble_services, like battery service or uart services.














2018年1月23日 星期二

nRF52840 in SDK14: disable NRF_LOG and activate UART

From SDK12, nRF5x series use NRF_LOG to replace printf( ) , which cause UART printf function can not be used. However, I still want to use UART function to print. This article is about how to disable NRF_LOG and use UART as usual.

This example is modified from ble_app_template
located in:
nRF5_SDK_14.2.0_17b948a\nRF5_SDK_14.2.0_17b948a\examples\ble_peripheral\ble_app_template

Board:nRF52840/SDK14/S140

STEP 0. project config

add  these files into nrf_library
"app_uart_fifo.c"
"app_fifo.c"
"retarget.c"


located in :
nRF5_SDK_14.2.0_17b948a\components\libraries\uart
nRF5_SDK_14.2.0_17b948a\components\libraries\fifo



next  include path to project option




STEP 1. modify "sdk_config"
I suggest first open sdk_config.h Text Editor

add these about line  3004 (after  nRF_Libraries )
//==========================================================
// <q> APP_FIFO_ENABLED  - app_fifo - Software FIFO implementation
 

#ifndef APP_FIFO_ENABLED
#define APP_FIFO_ENABLED 1
#endif


then add these about line 3080 (after APP_TWI_ENABLED )
// <e> APP_UART_ENABLED - app_uart - UART driver
//==========================================================
#ifndef APP_UART_ENABLED
#define APP_UART_ENABLED 1
#endif
// <o> APP_UART_DRIVER_INSTANCE  - UART instance used
 
// <0=> 0 

#ifndef APP_UART_DRIVER_INSTANCE
#define APP_UART_DRIVER_INSTANCE 0
#endif


then add these about line 3506(after NRF_QUEUE_ENABLED)
// <q> RETARGET_ENABLED  - retarget - Retargeting stdio functions
 

#ifndef RETARGET_ENABLED
#define RETARGET_ENABLED 1
#endif


Then open sdk_config.h Configuration Wizard 
you can see:






Then open nRF_Drivers and check:



but if you prefer to add code , please add in sdk_config.h Text Editor
about line  2724 (after TWI_ENABLE)
// <e> UART_ENABLED - nrf_drv_uart - UART/UARTE peripheral driver
//==========================================================
#ifndef UART_ENABLED
#define UART_ENABLED 1
#endif
// <o> UART_DEFAULT_CONFIG_HWFC  - Hardware Flow Control
 
// <0=> Disabled 
// <1=> Enabled 

#ifndef UART_DEFAULT_CONFIG_HWFC
#define UART_DEFAULT_CONFIG_HWFC 0
#endif

// <o> UART_DEFAULT_CONFIG_PARITY  - Parity
 
// <0=> Excluded 
// <14=> Included 

#ifndef UART_DEFAULT_CONFIG_PARITY
#define UART_DEFAULT_CONFIG_PARITY 0
#endif

// <o> UART_DEFAULT_CONFIG_BAUDRATE  - Default Baudrate
 
// <323584=> 1200 baud 
// <643072=> 2400 baud 
// <1290240=> 4800 baud 
// <2576384=> 9600 baud 
// <3862528=> 14400 baud 
// <5152768=> 19200 baud 
// <7716864=> 28800 baud 
// <10289152=> 38400 baud 
// <15400960=> 57600 baud 
// <20615168=> 76800 baud 
// <30801920=> 115200 baud 
// <61865984=> 230400 baud 
// <67108864=> 250000 baud 
// <121634816=> 460800 baud 
// <251658240=> 921600 baud 
// <268435456=> 1000000 baud 

#ifndef UART_DEFAULT_CONFIG_BAUDRATE
#define UART_DEFAULT_CONFIG_BAUDRATE 30801920
#endif

// <o> UART_DEFAULT_CONFIG_IRQ_PRIORITY  - Interrupt priority
 

// <i> Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice
// <0=> 0 (highest) 
// <1=> 1 
// <2=> 2 
// <3=> 3 
// <4=> 4 
// <5=> 5 
// <6=> 6 
// <7=> 7 

#ifndef UART_DEFAULT_CONFIG_IRQ_PRIORITY
#define UART_DEFAULT_CONFIG_IRQ_PRIORITY 7
#endif

// <q> UART_EASY_DMA_SUPPORT  - Driver supporting EasyDMA
 

#ifndef UART_EASY_DMA_SUPPORT
#define UART_EASY_DMA_SUPPORT 1
#endif

// <q> UART_LEGACY_SUPPORT  - Driver supporting Legacy mode
 

#ifndef UART_LEGACY_SUPPORT
#define UART_LEGACY_SUPPORT 1
#endif

// <e> UART0_ENABLED - Enable UART0 instance
//==========================================================
#ifndef UART0_ENABLED
#define UART0_ENABLED 1
#endif
// <q> UART0_CONFIG_USE_EASY_DMA  - Default setting for using EasyDMA
 

#ifndef UART0_CONFIG_USE_EASY_DMA
#define UART0_CONFIG_USE_EASY_DMA 1
#endif

// </e>

// <e> UART1_ENABLED - Enable UART1 instance
//==========================================================
#ifndef UART1_ENABLED
#define UART1_ENABLED 0
#endif
// <q> UART1_CONFIG_USE_EASY_DMA  - Default setting for using EasyDMA
 

#ifndef UART1_CONFIG_USE_EASY_DMA
#define UART1_CONFIG_USE_EASY_DMA 1
#endif

// </e>

// </e>

// <e> USBD_ENABLED - nrf_drv_usbd - USB driver
//==========================================================
#ifndef USBD_ENABLED
#define USBD_ENABLED 0
#endif
// <o> USBD_CONFIG_IRQ_PRIORITY  - Interrupt priority
 

// <i> Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice
// <0=> 0 (highest) 
// <1=> 1 
// <2=> 2 
// <3=> 3 
// <4=> 4 
// <5=> 5 
// <6=> 6 
// <7=> 7 

#ifndef USBD_CONFIG_IRQ_PRIORITY
#define USBD_CONFIG_IRQ_PRIORITY 7
#endif



STEP 2. disable NRF_LOG in UART
open sdk_config.h Configuration Wizard and check nrf_log Logger
disable them all






























STEP 3. modify main.c

add these include & #define in main.c
#include "app_uart.h"
#if defined (UART_PRESENT)
#include "nrf_uart.h"
#endif
#if defined (UARTE_PRESENT)
#include "nrf_uarte.h"
#endif
 
#define UART_HWFC APP_UART_FLOW_CONTROL_DISABLED
#define UART_TX_BUF_SIZE 256                         /**< UART TX buffer size. */
#define UART_RX_BUF_SIZE 256                         /**< UART RX buffer size. */


add below code  after buttons_leds_init( ) function :
void uart_error_handle(app_uart_evt_t * p_event)
{
    if (p_event->evt_type == APP_UART_COMMUNICATION_ERROR)
    {
        APP_ERROR_HANDLER(p_event->data.error_communication);
    }
    else if (p_event->evt_type == APP_UART_FIFO_ERROR)
    {
        APP_ERROR_HANDLER(p_event->data.error_code);
    }
}


static void uart_init(void)
{
  uint32_t err_code;
  const app_uart_comm_params_t comm_params =
  {
   RX_PIN_NUMBER,
   TX_PIN_NUMBER,
   RTS_PIN_NUMBER,
   CTS_PIN_NUMBER,
   UART_HWFC,
   false,
   NRF_UART_BAUDRATE_115200
  };

    APP_UART_FIFO_INIT(&comm_params,
                         UART_RX_BUF_SIZE,
                         UART_TX_BUF_SIZE,
                         uart_error_handle,
                         APP_IRQ_PRIORITY_LOWEST,
                         err_code);

    APP_ERROR_CHECK(err_code);
}


in main( ) function
please #if(0) log_init( ) and add uart_init( ) as this :

int main(void)
{
    bool erase_bonds;

    // Initialize.
#if(0) 
    log_init();
#endif 
    timers_init();  
    buttons_leds_init(&erase_bonds);
 
    uart_init();   
    printf("agathakuan\r\n");
    printf("system start!!\r\n");
 
    ble_stack_init();
    gap_params_init();
    gatt_init();
    advertising_init();
    services_init();
    conn_params_init();
    peer_manager_init();

    // Start execution.
    NRF_LOG_INFO("Template example started.");
    application_timers_start();

    advertising_start(erase_bonds);

    // Enter main loop.
    for (;;)
    {
        if (NRF_LOG_PROCESS() == false)
        {
            power_manage();
        }
    }
}

program and reset your device, then open your COM port, you can see:




NOTE:
1. in SDK14, the developer must assign UART, PWM,WDT...etc in sdk_config.h
otherwise, even they include all the paths and files needed, they still can not open UART.

2. if you activate NRF_LOG_BACKEND_UART, you should not use printf( ), else it will cause system fatal error.

3.The way to activate UART in SDK14 is NOT the same as SDK12


REFERENCE:
http://gaiger-programming.blogspot.tw/2016/12/nrf51-make-printf-works-well.html?m=1
http://programnoteagatha.blogspot.tw/2017/05/nrf52-printf-in-sdk12.html
























2018年1月18日 星期四

JN5169: software-implement I2C example with mpu6050

JN5169 hardware I2c pins are P0_14 & P0_15, which is the same as UART_1, so in some cases I have enabled pin 14,15 as UART_1,  I use 2 GPIOs (DIO in JN5169) to implement I2c, and print MPU6050 data.

my example is based on :
JN-AN-1171-ZigBee-LightLink-Demo/ Controller_ColorController


and I set :
pin 16 as SCL
pin 17 as SDA

STEP 0. my source code
myiic.h
/* myIIC.h
 *
 *  Created on: 2018.1.19
 *      Author: Agatha Kuan
 */

#ifndef MY_IIC_H_
#define MY_IIC_H_

#include <jendefs.h>
#include "zps_gen.h"
#include "AppHardwareApi.h"
#include <string.h>
#include "dbg.h"
#include "os.h"
#include "os_gen.h"



#define IIC_DATA_LEN    7
#define SI_TX      FALSE
#define SI_RX      TRUE

#define HW_DIO_CLK   16
#define HW_DIO_DATA   17
#define SI_CLK    (1<<HW_DIO_CLK)
#define SI_DATA    (1<<HW_DIO_DATA)
#define SI_SET_DELAY  6


#define BV(x)    (1<<(x))

#define SI_CLK_SET   vAHI_DioSetOutput(SI_CLK, 0)
#define SI_CLK_CLR   vAHI_DioSetOutput(0, SI_CLK)
#define SI_DATA_SET   vAHI_DioSetOutput(SI_DATA, 0)
#define SI_DATA_CLR   vAHI_DioSetOutput(0, SI_DATA)


PUBLIC void uI2C_Init ( void );

PUBLIC void uI2C_DIOConfig( bool_t wr );
PUBLIC void uI2C_Stop( void );
PUBLIC void uI2C_Start( void );
PUBLIC uint8 uI2C_Read_Data( bool_t tail );
PUBLIC void uI2C_Write_Data( uint8 ch );
PUBLIC void uI2C_Send_Ack( bool_t tail );
PUBLIC void uI2C_Read_Ack( void );

PUBLIC void uI2C_Read( uint8 *data, uint8 size );
PUBLIC void uI2C_Write( uint8 addr, uint8 *data, uint8 size );
PUBLIC void wDelayus(uint32 udelayus);


#endif /* MY_IIC_H_ */


myiic.c
/*
 * myiic.c
 *
 *  Created on: 2018.1.19
 *      Author: Agatha Kuan
 */

#include "myiic.h"


PUBLIC void uI2C_Stop( void );
PUBLIC void uI2C_Start( void );
PUBLIC uint8 uI2C_Read_Data( bool_t tail );
PUBLIC void uI2C_Write_Data( uint8 ch );
PUBLIC void uI2C_DIOConfig( bool_t wr );
PUBLIC void uI2C_Send_Ack( bool_t tail );
PUBLIC void uI2C_Read_Ack( void );

uint8 si_buff[32];


PUBLIC void uI2C_Init( void )
{
 vAHI_DioSetDirection(0, SI_CLK|SI_DATA);
 uI2C_Stop();
}


PUBLIC void uI2C_DIOConfig( bool_t wr )
{
 if ( wr )
 {
  /* read - input */
  vAHI_DioSetDirection(SI_DATA, 0);
 }
 else
 {
  /* write */
  vAHI_DioSetDirection(0, SI_DATA);
 }
}


PUBLIC void uI2C_Write_Data( uint8 ch )
{
 int idx;

 uI2C_DIOConfig( SI_TX );

 for (idx = 7; idx >= 0; idx-- )
 {
  if ((ch & BV(idx)) == BV(idx))
  {
   SI_DATA_SET;
   wDelayus(SI_SET_DELAY);
  }
  else
  {
   SI_DATA_CLR;
   wDelayus(SI_SET_DELAY);
  }
  SI_CLK_SET;
  wDelayus(SI_SET_DELAY);
  SI_CLK_CLR;
  wDelayus(SI_SET_DELAY);
 }
 /* wait for ack */
 uI2C_Read_Ack();
}

PUBLIC uint8 uI2C_Read_Data( bool_t tail )
{
 uint8 ret = 0;
 int x;
 volatile uint32 bitmap = 0;

 uI2C_DIOConfig( SI_RX );

 for ( x = 7; x >= 0; x-- )
 {
  ret <<= 1;
  SI_CLK_SET;
  wDelayus(SI_SET_DELAY);

  bitmap = u32AHI_DioReadInput();
  if ( (bitmap & BV(HW_DIO_DATA)) == BV(HW_DIO_DATA) )
  {
   ret |= 1;
  }

  SI_CLK_CLR;
  wDelayus(SI_SET_DELAY);
 }

 uI2C_Send_Ack( tail );
 return ret;
}

PUBLIC void uI2C_Start( void )
{
 SI_DATA_SET;
 SI_CLK_SET;
 wDelayus(SI_SET_DELAY);
 SI_DATA_CLR;
 wDelayus(SI_SET_DELAY);
 SI_CLK_CLR;
 wDelayus(SI_SET_DELAY);
}

PUBLIC void uI2C_Stop( void )
{
 SI_DATA_CLR;
 SI_CLK_SET;
 wDelayus(SI_SET_DELAY);
 SI_DATA_SET;
 wDelayus(SI_SET_DELAY);
}
PUBLIC void uI2C_Send_Ack( bool_t tail )
{
 uI2C_DIOConfig( SI_TX );

 if ( tail )
  SI_DATA_SET;
 else
  SI_DATA_CLR;
 wDelayus(SI_SET_DELAY);
 SI_CLK_SET;
 wDelayus(SI_SET_DELAY);
 SI_CLK_CLR;
 wDelayus(SI_SET_DELAY);

 uI2C_DIOConfig( SI_RX );
}

PUBLIC void uI2C_Read_Ack( void )
{
 uI2C_DIOConfig( SI_RX );

 SI_CLK_SET;
 wDelayus(SI_SET_DELAY);
 /* read slave ack */

 SI_CLK_CLR;
 wDelayus(SI_SET_DELAY);

 uI2C_DIOConfig( SI_TX );
}
                                                 
PUBLIC void wDelayus(uint32 udelayus)
{
    volatile uint32 u32Delay = 0;
    for(u32Delay=0;u32Delay< udelayus;u32Delay++);
}


and use this "myiic.c", I try to enable MPU6050 as IIC slave of JN5169.
and this is mpu6050.h

/*
 * mpu6050.h
 *
 * Created on: 2018.01.03
 * Author: Agatha Kuan
 */
 
#ifndef MPU6050_H_
#define MPU6050_H_

#include <jendefs.h>
#include "zps_gen.h"
#include "AppHardwareApi.h"
#include <string.h>
#include "dbg.h"
#include "os.h"
#include "os_gen.h"
#include "myiic.h"

#define MPU_SELF_TESTX_REG  0X0D 
#define MPU_SELF_TESTY_REG  0X0E 
#define MPU_SELF_TESTZ_REG  0X0F 
#define MPU_SELF_TESTA_REG  0X10 
#define MPU_SAMPLE_RATE_REG  0X19 
#define MPU_CFG_REG    0X1A 
#define MPU_GYRO_CFG_REG  0X1B 
#define MPU_ACCEL_CFG_REG  0X1C 

#define MPU_FIFO_EN_REG   0X23 
#define MPU_I2CMST_CTRL_REG  0X24 
#define MPU_I2CSLV0_REG   0X26 
#define MPU_I2CSLV1_ADDR_REG 0x28 
#define MPU_I2CSLV1_REG   0X29 
#define MPU_I2CSLV1_CTRL_REG 0X2A 
#define MPU_I2CSLV2_REG   0X2C 
#define MPU_I2CSLV2_CTRL_REG 0X2D 
#define MPU_I2CSLV3_REG   0X2F 
#define MPU_I2CSLV3_CTRL_REG 0X30 
#define MPU_I2CSLV4_ADDR_REG 0X31 
#define MPU_I2CSLV4_DO_REG  0X33 
#define MPU_I2CSLV4_REG   0X32 
#define MPU_I2CSLV4_DO_REG  0X33 
#define MPU_I2CSLV4_CTRL_REG 0X34 
#define MPU_I2CMST_STA_REG  0X36 
#define MPU_INTBP_CFG_REG  0X37 
#define MPU_INT_EN_REG   0X38 
#define MPU_INT_STA_REG   0X3A 


#define MPU_ACCEL_XOUTH_REG  0X3B 
#define MPU_ACCEL_XOUTL_REG  0X3C 
#define MPU_ACCEL_YOUTH_REG  0X3D 
#define MPU_ACCEL_YOUTL_REG  0X3E 
#define MPU_ACCEL_ZOUTH_REG  0X3F 
#define MPU_ACCEL_ZOUTL_REG  0X40 

#define MPU_TEMP_OUTH_REG  0X41 

#define MPU_GYRO_XOUTH_REG  0X43 
#define MPU_GYRO_XOUTL_REG  0X44 
#define MPU_GYRO_YOUTH_REG  0X45 
#define MPU_GYRO_YOUTL_REG  0X46 
#define MPU_GYRO_ZOUTH_REG  0X47 
#define MPU_GYRO_ZOUTL_REG  0X48 

#define MPU_I2CSLV0_DO_REG  0X63 
#define MPU_I2CSLV1_DO_REG  0X64 
#define MPU_I2CSLV2_DO_REG  0X65 
#define MPU_I2CSLV3_DO_REG  0X66 
#define MPU_I2CMST_DELAY_REG 0X67 

#define MPU_MDETECT_CTRL_REG 0X69 
#define MPU_USER_CTRL_REG  0X6A 
#define MPU_PWR_MGMT1_REG  0X6B 
#define MPU_PWR_MGMT2_REG  0X6C 

#define MPU_FIFO_CNTH_REG  0X72 
#define MPU_FIFO_CNTL_REG  0X73 
#define MPU_FIFO_RW_REG   0X74 

#define MPU_DEVICE_ID_REG  0X75 

#define MPU_ADDR    0X68
#define WHO_I_AM    0X68


PUBLIC uint8 mpu_init(void);
PUBLIC void mpu_get_accel(short *ax, short *ay, short *az);
PUBLIC void mpu_get_gyro(short *gx,short *gy,short *gz);
PUBLIC void mpu_get_temperature(short *temp);

PUBLIC uint8 mpu_restart(void);

#endif

mpu6050.c

/*
 * MPU6050.c
 *
 * Created on: 2018.01.03
 * Author: Agatha Kuan
 */
 
#include "myiic.h"
#include "mpu6050.h"

uint8 si_buff[32];


PUBLIC void mpu_read(uint8 register_addr, uint8 *data, uint8 size);
PUBLIC void mpu_write(uint8 register_addr, uint8 *data, uint8 size);
PUBLIC uint8 mpu_write_byte(uint8 register_addr, uint8 data);
PUBLIC uint8 mpu_set_rate(uint16 rate);
PUBLIC uint8 mpu_set_accel_fsr(uint8 fsr);
PUBLIC uint8 mpu_set_gyro_fsr(uint8 fsr);
PUBLIC uint8 mpu_init(void);


PUBLIC uint8 mpu_init(void)
{
 uint8 ret =0;

 mpu_write_byte(MPU_PWR_MGMT1_REG,0x80);
 wDelayus(100*100);
 mpu_write_byte(MPU_PWR_MGMT1_REG,0x00);

 mpu_set_gyro_fsr(3);
 mpu_set_accel_fsr(0);
 mpu_set_rate(50);

 mpu_write_byte(MPU_INT_EN_REG,0x00); 
 mpu_write_byte(MPU_USER_CTRL_REG,0x00); 
 mpu_write_byte(MPU_FIFO_EN_REG,0x00); 
 mpu_write_byte(MPU_INTBP_CFG_REG,0x80); 
 
 mpu_read(MPU_DEVICE_ID_REG,&ret,1);
 
 DBG_vPrintf(TRUE,"\r\nAgatha:Device mpu6050:%0x\r\n",ret);

 if(ret==WHO_I_AM)
 {
  mpu_write_byte(MPU_PWR_MGMT1_REG,0X01); 
  mpu_write_byte(MPU_PWR_MGMT2_REG,0X00); 
  mpu_set_rate(50);
 }
 else
  return 1;
 
 return 0;
}


PUBLIC uint8 mpu_restart(void)
{
 uint8 ret =0;
 

 mpu_read(MPU_DEVICE_ID_REG,&ret,1);
 if(ret==WHO_I_AM)
 {
  mpu_write_byte(MPU_PWR_MGMT1_REG,0X01); 
  mpu_write_byte(MPU_PWR_MGMT2_REG,0X00); 
  mpu_set_rate(50);
 }

 return 0;
}


PUBLIC uint8 mpu_set_gyro_fsr(uint8 fsr)
{
 uint8 data = 0;

 data =  fsr<<3;
 mpu_write(MPU_GYRO_CFG_REG,&data,1);
 return data;
}


PUBLIC uint8 mpu_set_accel_fsr(uint8 fsr)
{
 uint8 data = 0;

 data =  fsr<<3;
 mpu_write(MPU_ACCEL_CFG_REG,&data,1);

 return data;
}


PUBLIC uint8 mpu_set_lpf(uint16 lpf)
{
 uint8 data = 0;
 if(lpf>=188)data=1;
 else if(lpf>=98)data=2;
 else if(lpf>=42)data=3;
 else if(lpf>=20)data=4;
 else if(lpf>=10)data=5;
 else data=6; 

 mpu_write(MPU_CFG_REG,&data,1);

 return data;
}


PUBLIC uint8 mpu_set_rate(uint16 rate)
{
 uint8 data = 0;
 if(rate>1000)
  rate=1000;
 if(rate<4)rate=4;
  data=1000/rate-1;
 
 mpu_write(MPU_SAMPLE_RATE_REG,&data,1);

 return mpu_set_lpf(rate/2);
}

PUBLIC uint8 mpu_write_byte(uint8 register_addr, uint8 data)
{
 uI2C_Start();
 uI2C_Write_Data( (MPU_ADDR<<1)|0 );
 
 uI2C_Write_Data( register_addr );
 uI2C_Write_Data( data );
 
 uI2C_Stop();
 return 0;
}


PUBLIC void mpu_write(uint8 register_addr, uint8 *data, uint8 size)
{
 uint8 i;

 uI2C_DIOConfig( SI_TX );
 
 uI2C_Start();
 
 uI2C_Write_Data( (MPU_ADDR<<1)|0 ); 
 uI2C_Write_Data( register_addr );
 
 for (i = 0; i<size; i++)
 {
  uI2C_Write_Data( data[i] );
 }
 
 uI2C_Stop();
}


PUBLIC void mpu_read(uint8 register_addr, uint8 *data, uint8 size)
{
 uint8 i;

 uI2C_DIOConfig( SI_TX );
 
 uI2C_Start();
 
 uI2C_Write_Data( (MPU_ADDR<<1)|0 ); 
 uI2C_Write_Data( register_addr );
 
 uI2C_Start();
 
 uI2C_Write_Data( (MPU_ADDR<<1)|1);
 
 for (i = 0; i<size; i++)
 {
  if ( i == size-1 )
   si_buff[i] = uI2C_Read_Data( TRUE );
  else
   si_buff[i] = uI2C_Read_Data( FALSE );
 }
 
 uI2C_Stop();

 memcpy( data, si_buff, size );
 
}


PUBLIC void mpu_get_accel(short *ax, short *ay, short *az)
{
 uint8 ret[6];
 mpu_read(MPU_ACCEL_XOUTH_REG,&ret[0],6);

 *ax=((uint16)ret[0]<<8)|ret[1];
 *ay=((uint16)ret[2]<<8)|ret[3];
 *az=((uint16)ret[4]<<8)|ret[5];
}


PUBLIC void mpu_get_gyro(short *gx,short *gy,short *gz)
{
 uint8 ret[6];
 mpu_read(MPU_GYRO_XOUTH_REG,&ret[0],6);

 *gx=((uint16)ret[0]<<8)|ret[1];
 *gy=((uint16)ret[2]<<8)|ret[3];
 *gz=((uint16)ret[4]<<8)|ret[5];
}


PUBLIC void mpu_get_temperature(short *temp)
{
 uint8 buf[2];
 short raw;
 mpu_read(MPU_TEMP_OUTH_REG,&buf[0],2);
 raw=((uint16)buf[0]<<8)|buf[1];

 raw = 36.53+((double)raw)/340;

 *temp = raw*100;
}


STEP 1.  include "myiic.h" & "mpu6050.h"  to zll_remote_node.c

1.
add these 4 files to
C:\NXP\bstudio_nxp\workspace\JN-AN-1171-ZigBee-LightLink-Demo_NXP\Common_Controller\Source




2.
go to C:\NXP\bstudio_nxp\workspace\JN-AN-1171-ZigBee-LightLink-Demo_NXP\Common_Controller\Build
and open Makefile


at about line.180
add
APPSRC += myiic.c
APPSRC += mpu6050.c
to compile them.

2.
inlcude "mpu6050.h" & "myiic.h" in zll_remote_node.h


#include "myiic.h"
#include "mpu6050.h"




3.
add the following in "zll_remote_node.c" vStartUpHW

void vStartUpHW(void)
{

    uint8 u8Status;
 
 /*Agatha: initialized software implement I2C*/
    uI2C_Init();
    mpu_init(); 

    /* Restart the keyboard scanning timer as we've come up through */
    /* warm start via the Power Manager if we get here              */
    APP_wButtonInitialise();
    vConfigureScanTimer();

     DBG_vPrintf(TRACE_SLEEP, "\nWoken: start poll timer,");
     u8Status = ZPS_eAplZdoPoll();
     DBG_vPrintf(TRACE_SLEEP, " Wake poll %02x\n", u8Status);
     OS_eStartSWTimer(APP_PollTimer, APP_TIME_MS(200), NULL);
}

in the same file OS_TASK(APP_SleepAndPollTask)
so the device will continuous read data 

OS_TASK(APP_SleepAndPollTask)
{
 uint32 u32Time = APP_TIME_MS(1000);
 
 if (!bTLinkInProgress)
 {
  DBG_vPrintf(TRUE, "KeepAlive - %d, Deep Sleep - %d fast Poll - %d Rejoin - %d FailedTojoin %d \n",
          u8KeepAliveTime,
          u8DeepSleepTime,
          u16FastPoll,
          u8RejoinAttemptsRemaining,
          bFailedToJoin);
    
  /*Agatha: read MPU6050 data*/
  mpu_restart();
  mpu_get_gyro(&gyro_x,&gyro_y,&gyro_z);
  DBG_vPrintf(TRUE,"%d,%d,%d\r\n",gyro_x,gyro_y,gyro_z);


  switch (sZllState.eNodeState)
  {
   case E_REMOTE_WAIT_START:
    DBG_vPrintf(TRACE_REMOTE_NODE|TRACE_REJOIN, "Poll and Sleep: Wait Start \n");
    /* wait to form or to join network, if nothing happens deep sleep */
    u32Time = APP_TIME_MS(1000);
    if (bFailedToJoin)
    {
     if(u8RejoinAttemptsRemaining == 0)
     {
      bFailedToJoin = FALSE;
      u8RejoinAttemptsRemaining = TRY_JOIN_FAIL_TIME;
      u8KeepAliveTime = KEEP_ALIVETIME;//KEEP_ALIVE_FACTORY_NEW
      DBG_vPrintf(TRACE_REJOIN, "Wait start: no more retries\n");
     }
     else
     {
      u8RejoinAttemptsRemaining--;
      DBG_vPrintf(TRACE_REJOIN, "Wait start: try a rejoin\n");
      vSetRejoinFilter();
      ZPS_eAplZdoRejoinNetwork(TRUE);
     }
    }
                  else
                  {
                   if(TESTDONE ==u8SaveFlash[0])
     {
      if(u8KeepAliveTime == 0)
      {
       /* waited long enough without forming or joining,
       * give up and deep sleep to save the  battery
       */
       vStopAllTimers();
       /*PWRM_vInit(E_AHI_SLEEP_DEEP);
       bDeepSleep = TRUE;
       DBG_vPrintf(TRACE_SLEEP, "Wait start: go deep\n");
       DBG_vPrintf(TRACE_SLEEP,"\n Activity %d\n",PWRM_u16GetActivityCount());
       */
       if (u8DeepSleepTime)
       {
        PWRM_eScheduleActivity(&sWake, (SLEEP_DURATION_MS * SLEEP_TIMER_TICKS_PER_MS) , vWakeCallBack);
        PWRM_vInit(E_AHI_SLEEP_OSCOFF_RAMON);
        DBG_vPrintf(TRACE_SLEEP, "poll task: schedule sleep\n");
       }
       else
       {
        PWRM_vInit(E_AHI_SLEEP_DEEP);
        bDeepSleep = TRUE;
        DBG_vPrintf(TRACE_SLEEP, "poll task: go deep\n");
       }
       DBG_vPrintf(TRACE_SLEEP,"\n Activity %d\n",PWRM_u16GetActivityCount());
       return;
      }
      else
      {
       if(!App_CheckButtonStatus())
       {
        u8KeepAliveTime--;
       }
      }
                   }
                  }
                  break;
    case E_REMOTE_NETWORK_DISCOVER:
        /* do nothing, wait for classic discovery and join to finish */
        u32Time = APP_TIME_MS(1000);
        DBG_vPrintf(TRACE_REMOTE_NODE|TRACE_REJOIN, "Poll and Sleep: Network Discovery\n");
        break;
    case E_REMOTE_RUNNING:
     DBG_vPrintf(TRACE_REMOTE_NODE|TRACE_REJOIN, "Poll and Sleep: Running\n");
     if (bFailedToJoin)
     {
      /* Manage rejoin attempts,then short wait, then deep sleep */
      u32Time = APP_TIME_MS(1000);
      if(u8RejoinAttemptsRemaining == 0)
      {
       if (u8DeepSleepTime) 
       {
        vStopAllTimers();
        PWRM_eScheduleActivity(&sWake,(SLEEP_DURATION_MS * SLEEP_TIMER_TICKS_PER_MS), vWakeCallBack);
        if (PWRM_u16GetActivityCount())
        {
         OS_eContinueSWTimer(APP_PollTimer, APP_TIME_MS(1000), NULL);
         u8DeepSleepTime--;
        }
        else
        {
         PWRM_vInit(E_AHI_SLEEP_OSCOFF_RAMON);
        }
       }
       else
       {
        vStopAllTimers();
        DBG_vPrintf(TRACE_REJOIN, "join failed: go deep... %d\n", PWRM_u16GetActivityCount());
        PWRM_vInit(E_AHI_SLEEP_DEEP);
        bDeepSleep = TRUE;
        return;
       }
      }
      else
      {
       if(!App_CheckButtonStatus())
       {
        u8RejoinAttemptsRemaining--;
        DBG_vPrintf(TRACE_REJOIN, "join failed: try a rejoin\n");
        vSetRejoinFilter();
        ZPS_eAplZdoRejoinNetwork(TRUE);
       }
      }
                   }
     else
     {
      /* Manage polling, then warm sleep, then deep sleep */
      if(u8KeepAliveTime == 0)
      {
       vStopAllTimers();
       if (u8DeepSleepTime)
       {
        PWRM_eScheduleActivity(&sWake, (SLEEP_DURATION_MS * SLEEP_TIMER_TICKS_PER_MS) , vWakeCallBack);
        PWRM_vInit(E_AHI_SLEEP_OSCOFF_RAMON);
        DBG_vPrintf(TRACE_SLEEP, "poll task: schedule sleep\n");
       }
       else 
       {
        PWRM_vInit(E_AHI_SLEEP_DEEP);
        bDeepSleep = TRUE;
        DBG_vPrintf(TRACE_SLEEP, "poll task: go deep\n");
       }
       DBG_vPrintf(TRACE_SLEEP,"Activity %d\n",PWRM_u16GetActivityCount());
       return;
      }
      else
      {
       uint8 u8PStatus;
       u8PStatus = ZPS_eAplZdoPoll();
       if ( 1 /*u8PStatus*/)
       {
        DBG_vPrintf(TRACE_REMOTE_NODE|TRACE_REJOIN, "\nPOLL status %d\n", u8PStatus);
       }
       if (u16FastPoll)
       {
        u16FastPoll--;
        u32Time = POLL_TIME_FAST, NULL;
        if (u16FastPoll == 0)
        {
         DBG_vPrintf(TRACE_REMOTE_NODE, "\nStop fast poll");
        }
       }
       else
       {
        /* Decrement the keep alive in the normal operation mode
        * Not in active scann mode or while fast polling
        */
        if(0 < u8KeepAliveTime)
        {
         if(!App_CheckButtonStatus())
         {
          u8KeepAliveTime--;
         }
        }
        /*Start Poll Timer to continue normal polling */
        u32Time = APP_TIME_MS(1000);
       }
      }
                   }
                   break;
   default:
    /* shouldb't happen, but... */
    u32Time = APP_TIME_MS(1000);
    break;
  }
 }
 else
 {
  DBG_vPrintf(TRACE_REMOTE_NODE|TRACE_REJOIN, "Sleep and Poll: Touch lnk in progress\n");
 }
 OS_eContinueSWTimer(APP_PollTimer, APP_TIME_MS(1000), NULL);
}
#endif

build the project Controller_ColorController, and  program to device, open UART COM of the device , you can see: