2015年4月26日 星期日

CC2540/CC2541 : Exploit Non-Volatile Memory for Changing Scan Response Data (SRP) Dynamically


     How to change the SRP data is a very imperative for most bluetooth low energy use case. But in official Texas Instruments(TI) example code, there is no demonstration  for this purpose. In here, I note how to reach the goal.

    In the TI forum , it sai possible to update RSP data by disabling/enabling advertising.  But for my feeble trying, the RSP do not be updated. So I use the "reboot" as my method.

   Before achieving the topic goal, I would like to refer to how to add uart receiving function in example SimpleBLEPeripheral. The is based on ghostyu's example (in simplified chinese).


0. Copy while SimpleBLEPeripheral folder as a new folder SimpleBLEPeripheral_SerialPrint.

1. Add those file in your IAR project:

serialcommunication.c :



#include "bcomdef.h"
#include "OSAL.h"
#include "OSAL_PwrMgr.h"

#include "OnBoard.h"
#include "hal_uart.h"
#include "hal_lcd.h"
#include "serialcommunication.h"

#if defined ( PLUS_BROADCASTER )
  #include "peripheralBroadcaster.h"
#else
  #include "peripheral.h"
#endif

void SerialInitTransport(void)
{
  halUARTCfg_t uartConfig;

  // configure UART
  uartConfig.configured           = TRUE;
  uartConfig.baudRate             = SBP_UART_BR;
  uartConfig.flowControl          = SBP_UART_FC;
  uartConfig.flowControlThreshold = SBP_UART_FC_THRESHOLD;//enable when flowControl is valid
  uartConfig.rx.maxBufSize        = SBP_UART_RX_BUF_SIZE;//UART RX 
  uartConfig.tx.maxBufSize        = SBP_UART_TX_BUF_SIZE;//UART TX
  uartConfig.idleTimeout          = SBP_UART_IDLE_TIMEOUT;
  uartConfig.intEnable            = SBP_UART_INT_ENABLE;//interrupt enable
  uartConfig.callBackFunc         = sbpSerialAppCallback;//uart callback function

  // start UART
  // Note: Assumes no issue opening UART port.
  (void)HalUARTOpen( SBP_UART_PORT, &uartConfig );

  return;
}/*serialAppInitTransport*/


uint16 numBytes;

void sbpSerialAppCallback(uint8 port, uint8 event)
{
  uint8  pktBuffer[SBP_UART_RX_BUF_SIZE];
  // unused input parameter; PC-Lint error 715.
  (void)event;
  HalLcdWriteString("Data form my UART:", HAL_LCD_LINE_4 );
  //return lengh
  if( (numBytes = Hal_UART_RxBufLen(port)) > 0)
  {      
      //int j;
        //read all data
 (void)HalUARTRead(port, pktBuffer, numBytes);
        //uint8 blankStr[HAL_LCD_MAX_CHARS];
       // memset(&blankStr[0], 32, HAL_LCD_MAX_CHARS);
                      
 HalLcdWriteString(pktBuffer, HAL_LCD_LINE_5 );
        
        //GAPRole_SetParameter( GAPROLE_SCAN_RSP_DATA, 
        //  numBytes, &pktBuffer[0] );
        
        //simpleProfileChar1 = pktBuffer[0];
  }/*if */
  
}/*sbpSerialAppCallback*/


static uint8 sendMsgTo_TaskID;

void SerialApp_Init( uint8 taskID )
{
  SerialInitTransport(); 
  sendMsgTo_TaskID = taskID; //save task id, for spare using.
}/*SerialApp_Init*/


serialcommunication.h


#ifndef _SERIAL_COMMUNICATION_H_
#define _SERIAL_COMMUNICATION_H_

#ifdef __cplusplus
extern "C"
{
#endif
  

#define SBP_UART_PORT                  HAL_UART_PORT_0
//#define SBP_UART_FC                    TRUE
#define SBP_UART_FC                    FALSE
#define SBP_UART_FC_THRESHOLD          48
#define SBP_UART_RX_BUF_SIZE           128
#define SBP_UART_TX_BUF_SIZE           128
#define SBP_UART_IDLE_TIMEOUT          6
#define SBP_UART_INT_ENABLE            TRUE
#define SBP_UART_BR                     HAL_UART_BR_57600 


// Serial Port Related
extern void SerialApp_Init(uint8 taskID);
extern void sbpSerialAppCallback(uint8 port, uint8 event);
void SerialInitTransport();

#ifdef __cplusplus
}
#endif

#endif/*_SERIAL_COMMUNICATION_H_*/


3. Modify simpleBLEPeripheral.c :

Add include  (about line 82):


#if defined FEATURE_OAD
  #include "oad.h"
  #include "oad_target.h"
#endif

#include "serialcommunication.h"
/*********************************************************************
 * MACROS
 */



Initialize uart (in function void SimpleBLEPeripheral_Init( uint8 task_id ), about line 286):


void SimpleBLEPeripheral_Init( uint8 task_id )
{
  simpleBLEPeripheral_TaskID = task_id;

  SerialApp_Init(task_id);
  // Setup the GAP



Now, you could use a serial com-port program(like sscom) to send data via uart.


    I assume the new SRP data is from UART, and received data be only the new SRP string. Modifying the UART callback function, to add some processing for save RSP values:


uint16 numBytes;

void sbpSerialAppCallback(uint8 port, uint8 event)
{
  uint8 pktBuffer[SBP_UART_RX_BUF_SIZE];
  // unused input parameter; PC-Lint error 715.
  (void)event;
  HalLcdWriteString("Data form my UART:", HAL_LCD_LINE_4 );
  
  //return length
  if( 0 < (numBytes = Hal_UART_RxBufLen(port)))
  {      
    
 /*that should use timer + process_event to update LCD content*/   
    //(void)HalUARTRead(port, pktBuffer, numBytes);        
 
 /* 
  baud rate = 57600-> byterate = 7200, it is, for one byte,  it needs 138 us 
  nop cast 1 clock, for CC254X, the crytal be 32MHz
  it is needed to be dalay over 138us ->  4416 nop operations
  
  if there is no delay in here, the uart-in data would be lost.
  
  most persons use 6000 (192us) as delay constant (a superstition?)
 */
    int i = 0;
    for(i = 0 ;0 < 6000;i++) 
  asm("nop");
  
  
 /*string sequence may be ended at 0x0A(enter) 0x0D(new line)*/
    if(0x0A == pData[numBytes - 1] || 0x0D == pData[numBytes - 1])
  numBytes--;
        
 if(0x0A == pData[numBytes - 2] || 0x0D == pData[numBytes - 2])
  numBytes--; 
 
        uint8 buffer[32];
        uint16 scanRspDataSize;
        
        memset(&buffer[0], 0, 32);                

        osal_snv_write(NV_MEM_ID, 32, &buffer[0]);
        
        SetRspData(&pData[0], numBytes, &buffer[0], &scanRspDataSize);              
        osal_snv_write(NV_MEM_ID, scanRspDataSize, &buffer[0]);                                  
        HAL_SYSTEM_RESET();          
  }/*if */
  
}/*sbpSerialAppCallback*/


  that is,clean the non-volatile memory, and save the organized RSP data, then reboot.

the NV_MEM_ID is 0xFE, it is my choice only. you could use other value.

the SetRspData function, which is for organizing data as RSP format, be :
(I wrote it in simpleBLEPeripheral.c)


void SetRspData(uint8 *pName, uint16 nameLen, 
                uint8 *pRspData, uint16 *pScanRspDataSize)
{       

  if(nameLen  > 31 - (2 + (5 + 1) + (1 + 2)) )
    nameLen =  31 - (2 + (5 + 1) + (1 + 2));
    
  pRspData[0] = nameLen + 1;
  pRspData[1] = GAP_ADTYPE_LOCAL_NAME_COMPLETE;
  memcpy(&pRspData[2], pName, nameLen); 
 
  int16  i;
  
  i = nameLen + 2; 
  pRspData[i++] = 0x05;
  pRspData[i++] = GAP_ADTYPE_SLAVE_CONN_INTERVAL_RANGE;
  pRspData[i++] = LO_UINT16( DEFAULT_DESIRED_MIN_CONN_INTERVAL );     
  pRspData[i++] = HI_UINT16( DEFAULT_DESIRED_MIN_CONN_INTERVAL );
  pRspData[i++] = LO_UINT16( DEFAULT_DESIRED_MAX_CONN_INTERVAL );     
  pRspData[i++] = HI_UINT16( DEFAULT_DESIRED_MAX_CONN_INTERVAL );
  
  pRspData[i++] = 0x02;
  pRspData[i++] =GAP_ADTYPE_POWER_LEVEL;
  pRspData[i++] = 0;       // 0dBm;    
  
  *pScanRspDataSize =  i;
}/*SetRspData*/


Now modify the void SimpleBLEPeripheral_Init( uint8 task_id ) in simpleBLEPeripheral.c, about line 315 :


 // Set the GAP Role Parameters    
    
    GAPRole_SetParameter( GAPROLE_ADVERT_ENABLED, sizeof( uint8 ), &initial_advertising_enable );
    GAPRole_SetParameter( GAPROLE_ADVERT_OFF_TIME, sizeof( uint16 ), &gapRole_AdvertOffTime );
       
    uint8 defaultName[32];
    
    memset(&defaultName[0], 0, 32);
    sprintf((char*)&defaultName[0], "defaultRSP");
    
    uint8 buffer[32];    
    memset(&buffer[0], 0, 32);
      
    uint8 ret;       
   
    uint16 scanRspDataSize;
    scanRspDataSize = 0;  

    ret = osal_snv_read(NV_MEM_ID, 32 , &buffer[0]);
   
    if(SUCCESS == ret )  
    {      
      int i;      
      scanRspDataSize = 0;       
      scanRspDataSize = 1 + buffer[0];      
      i = 1 + buffer[0];
      scanRspDataSize += 1 + buffer[i];
      i += 1 + buffer[i];      
      scanRspDataSize += 1 + buffer[i];
    }/*if*/    
    
    if(NV_OPER_FAILED == ret)
    {
      memset(&buffer[0], 0, 31);      
      SetRspData(&defaultName[0], strlen((char*)&defaultName[0]), 
                 &buffer[0], &scanRspDataSize);      
      //ret = osal_snv_read(0xfe, 32 , &buffer[0]);
      //osal_snv_write(0xfe, sizeof(scanRspData), &scanRspData[0]);
    }/*if */
       
    
    GAPRole_SetParameter( GAPROLE_SCAN_RSP_DATA, scanRspDataSize, &buffer[0] );



That is, to read the non-volatile memory: if there is no data, use default name, otherwise use saved data. That data would be used for SRP, the peripheral name.


If you use serial comport program to send a string into the CC2540 (or CC2541), the board would reboot, and the string be the new peripheral name.

Extra:

If you would like to print data to uart (that could use for echoing), the serialcommunication.c could be add function SerialPrintf :


// ref : http://bbs.elecfans.com/jishu_431223_2_1.html#comment_top

#ifdef HAL_UART_DMA_TX_MAX
  #define PRINT_BUF_LEN HAL                 UART_DMA_TX_MAX
#else
  #define PRINT_BUF_LEN                     (128)
#endif

int SerialPrintf(const char *fmt, ...)
{
    uint32  ulLen;
    va_list ap;

    char *pBuf;
    
    pBuf = (char*)osal_mem_alloc(PRINT_BUF_LEN);  
    
    va_start(ap, fmt);
    ulLen = vsprintf(pBuf, fmt, ap);       
    va_end(ap);

    HalUARTWrite(HAL_UART_PORT_0, (uint8*)pBuf, ulLen); 
    osal_mem_free(pBuf);    
    
    return ulLen;    
}/*SerialPrintf*/

And do not forget adding include head to avoid compiling warning.


#include "stdarg.h"
#include "stdio.h"


沒有留言:

張貼留言