2015年8月20日 星期四

CC2540/2541 : Change Advertising Data And Scan Response Without Rebooting


    From my previous article , I mention a method to change advertise data and scan response data by reboot. It is work, but it is not suit for change these 2 data frequently.

   In here, I note a way to change those WITHOUT rebooting. But the way needs to modify BLE stack code of Texas instrument. It may cause side effect which I do not know yet.

  
Step 0.
   Copy your SimpleBLEPeripheral folder in BLE stack to be SimpleBLEPeripheral_ChangeRSP.

Step 1.

 Move to the file simpleBLEPeripheral.c, Add calling the function RegisterForKeys in the beginning of SimpleBLEPeripheral_Init:


void SimpleBLEPeripheral_Init( uint8 task_id )
{
  simpleBLEPeripheral_TaskID = task_id;

#ifdef HAL_KEY  
   RegisterForKeys( task_id );
#endif




  Remove the preprocessor for CC2540_MINIK, about line 229


//#if defined( CC2540_MINIDK )
static void simpleBLEPeripheral_HandleKeys( uint8 shift, uint8 keys );
//#endif


And line 527:


static void simpleBLEPeripheral_ProcessOSALMsg( osal_event_hdr_t *pMsg )
{
  switch ( pMsg->event )
  {
 // #if defined( CC2540_MINIDK )
    case KEY_CHANGE:
      simpleBLEPeripheral_HandleKeys( ((keyChange_t *)pMsg)->state, ((keyChange_t *)pMsg)->keys );
      break;
 // #endif // #if defined( CC2540_MINIDK )


Obviously, I will re-implement the function

static void simpleBLEPeripheral_HandleKeys( uint8 shift, uint8 keys )


To trigger advertise data and scan response data changing.

The function simpleBLEPeripheral_HandleKeys I implemented as :


#include "string.h"
#include "stdio.h" 

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*/

/*********************************************************************
 * @fn      simpleBLEPeripheral_HandleKeys
 *
 * @brief   Handles all key events for this device.
 *
 * @param   shift - true if in shift/alt.
 * @param   keys - bit field for key events. Valid entries:
 *                 HAL_KEY_SW_2
 *                 HAL_KEY_SW_1
 *
 * @return  none
 */


static void simpleBLEPeripheral_HandleKeys( uint8 shift, uint8 keys )
{ 
  
  /*ingnore the signal which be caused by stick back to original position*/  
  if(HAL_KEY_STATE_SHIFT == shift)  
    return ;
       
  uint8 rspData[32];                  
  uint8 buffer[20]; /*max rsp lengh be 20*/
  
  uint16 scanRspDataSize;
  
  scanRspDataSize = 0;  
  osal_memset(&rspData[0], 0, 32);   
  osal_memset(&buffer[0], 0, 20);   
  
  
  if ( HAL_KEY_UP & keys )
    sprintf((char*)&buffer[0], "%s", "SimpleBLEPeriUp");
  else if (HAL_KEY_DOWN & keys )  
    sprintf((char*)&buffer[0], "%s", "SimpleBLEPeriDown");
  else if (HAL_KEY_LEFT & keys )  
    sprintf((char*)&buffer[0], "%s", "SimpleBLEPeriLeft");
  else if (HAL_KEY_RIGHT & keys )  
    sprintf((char*)&buffer[0], "%s", "SimpleBLEPeriRight");
  else if (HAL_KEY_CENTER & keys )  
    sprintf((char*)&buffer[0], "%s", "SimpleBLEPeriCenter");
       
  
  uint8 adv_status;
  
  adv_status = FALSE;
  GAPRole_SetParameter( GAPROLE_ADVERT_ENABLED, 
                       sizeof( uint8 ), &adv_status );
  
  SetRspData((uint8*)&buffer[0], strlen((char*)&buffer[0]), 
               &rspData[0], &scanRspDataSize);                                                                  
  GAPRole_SetParameter( GAPROLE_SCAN_RSP_DATA, scanRspDataSize, &rspData[0]);  

  adv_status = TRUE;
  GAPRole_SetParameter( GAPROLE_ADVERT_ENABLED, 
                       sizeof( uint8 ), &adv_status );
  
  HalLcdWriteString( (char*)&buffer[0],  HAL_LCD_LINE_1);        
}/*simpleBLEPeripheral_HandleKeys*/


To replace the original one, about line 544.

And Add HAL_KEY=TRUE in the preprocessor define (not necessary).

    To here, you could found that string displayed in LCD would change when you nudge the joystick. But the peripheral name stills.

Step 2

  Add the line in file peripheral.c, about line  299:

 case GAPROLE_SCAN_RSP_DATA:
      if ( len <= B_MAX_ADV_LEN )
      {
        VOID osal_memset( gapRole_ScanRspData, 0, B_MAX_ADV_LEN );
        VOID osal_memcpy( gapRole_ScanRspData, pValue, len );
        gapRole_ScanRspDataLen = len;
        
        //GAIGER
        GAP_UpdateAdvertisingData(gapRole_TaskID,
                              TRUE, gapRole_AdvertDataLen, gapRole_AdvertData );
      }
      else
      {
        ret = bleInvalidRange;
      }      

  Then you would find the Scan Response Data would change as the joystick.

   Follow above suit, you could change advertise data without reboot, by modify the same file periphereral.c, about line 289:


 case GAPROLE_ADVERT_DATA:
      if ( len <= B_MAX_ADV_LEN )
      {
        VOID osal_memset( gapRole_AdvertData, 0, B_MAX_ADV_LEN );
        VOID osal_memcpy( gapRole_AdvertData, pValue, len );
        gapRole_AdvertDataLen = len;
        
        //GAIGER
        GAP_UpdateAdvertisingData(gapRole_TaskID,
                              TRUE, gapRole_AdvertDataLen, gapRole_AdvertData );
      }
      else
      {
        ret = bleInvalidRange;
      }
      break;



Note:
  The sequence of "stop advertising - set RSP DATA - start advertise" in my implement is not requisite, you could simplify it as setting RSP DATA only.

  But, when you remove those code, the status displayed in the LCD would be "Error" while you push the joystick, Though your cc254x works well.That is, the function 


if ( GAP_MakeDiscoverable( gapRole_TaskID, &params ) != SUCCESS )

returning bleAlreadyInRequestedMode(0x11) in peripheral.c about line 754.

  The "Error" is inevitable if you change advertise data dynamically, even there is "stop-set advertise data - start" sequence in code. But you could ingore it by mending the code as :


      params.channelMap = gapRole_AdvChanMap;
      params.filterPolicy = gapRole_AdvFilterPolicy;
     
      uint8 ret;
      
      ret = GAP_MakeDiscoverable( gapRole_TaskID, &params );      
       //GAIGER
      if(bleAlreadyInRequestedMode == ret)
        ret = SUCCESS;
      
      if ( ret != SUCCESS )
      {
        gapRole_state = GAPROLE_ERROR;
        
        // Notify the application with the new state change

 ( I use BLE stack 1.3.2, maybe the patch is redundant in newer version.)


 This article refer to ghostyu's cc254xek development suite :  : tutorial document level 4 : BLE actual practice , Section 10:
       Bluetooth Weather station - Based on Humidity & Temperature Sensor  SHT20 with I2C Interface (In simplified Chinese).

2015年8月5日 星期三

D-Bus : Transmit a Data Array in Simple and Useful Form



     In lots situation, One would send data via a simple data structure :  a byte array contain real data, and a integer to note this data's length. This concept would appear when one uses the D-Bus, of Course.  For D-Bus, D-BUS_TYPE_ARRAY is different from the others data types, for it is not way to know the data length if there is no additional information. (length of DBUS_TYPE_STRING could be known by seeking where is the null flag.)

    In this example, I demonstrate How to send a data array via D-Bus.

   server.c :


 
/* server.c */

#include <dbus/dbus.h>
#include <stdbool.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

DBusHandlerResult filter_func(DBusConnection *connection, 
      DBusMessage *message, void *usr_data)
{
    DBusMessage *reply;
    dbus_bool_t handled = false;
    char *pReadData;
    int len;
    unsigned char i;
     
    DBusError dberr;
    
    dbus_error_init(&dberr);
    
    printf("pReadData = %x\n", (unsigned int)pReadData);
    
    if(FALSE == dbus_message_get_args(message, &dberr, DBUS_TYPE_ARRAY,
  DBUS_TYPE_BYTE, &pReadData, &len,  DBUS_TYPE_INVALID) && 0 != len)
 {
  //printf("len = %d\n");
  //printf("receiver data error\n");
  return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 }
 
 if(0 == len)
  return DBUS_HANDLER_RESULT_HANDLED;
    
    printf("len = %d, ", len);
    
    for( i = 0; i < len; i++)
  printf("%#2x ", (unsigned char)pReadData[i]);
 printf("\n");    
    handled = true;
    
    printf("pReadData = %x\n", (unsigned int)pReadData);
    
    /*if one free pReadData, it will crash!*/
    //dbus_free_string_array((char**)&pReadData);
    
    return (handled ? DBUS_HANDLER_RESULT_HANDLED :
  DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
  
}/*filter_func*/


int main(int argc, char *argv[])
{
    DBusError dberr;
    DBusConnection *dbconn;

    dbus_error_init(&dberr);
    dbconn = dbus_bus_get(DBUS_BUS_SESSION, &dberr);

    if (!dbus_connection_add_filter(dbconn, filter_func, NULL, NULL)) {
        return -1;
    }

    dbus_bus_add_match(dbconn, "type='signal',interface='gaiger.Drstein.Demonstration'", &dberr);
    
    while(dbus_connection_read_write_dispatch(dbconn, -1)) {
        /* loop */
    }
    
    return 0;
}/*main*/


    Take a notice: the buffer which containers received data is allocated by D-Bus, you could not release it manually. D-Bus would manage this buffer automatically, it would not occur memory leaking.


client.c :


#include <dbus/dbus.h>
#include <stdbool.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int db_send(DBusConnection *dbconn)
{
    DBusMessage *dbmsg;
    char *pSendData;
    int len;
    unsigned char i;

 pSendData = (char *)malloc(256);
 
    dbmsg = dbus_message_new_signal("/client/signal/Object", 
  "gaiger.Drstein.Demonstration", "Test");
  
 len = 6;
 for(i = 0; i < len; i++)
  pSendData[i] = (unsigned char)i;
   
    if (!dbus_message_append_args(dbmsg, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, 
  &pSendData,  len, DBUS_TYPE_INVALID)) 
 {
  return -1;
    }

    if (!dbus_connection_send(dbconn, dbmsg, NULL)) {
        return -1;
    }
    
    dbus_connection_flush(dbconn);
    printf("send message : len = %d, ", len );
    
 for( i = 0; i < len; i++)
  printf("%#x ", (unsigned char)pSendData[i]);
    
 printf("\n"); 
    dbus_message_unref(dbmsg);    
    free(pSendData);
   
    return 0;
}/**/


int main(int argc, char *argv[])
{
 unsigned int i;
    DBusError dberr;
    DBusConnection *dbconn;

    dbus_error_init(&dberr);

    dbconn = dbus_bus_get(DBUS_BUS_SESSION, &dberr);

#if(1)    
    for(i = 0; i < 3; i++)
  db_send(dbconn);
#else
    while(dbus_connection_read_write_dispatch(dbconn, -1)) {
        db_send(dbconn);
    }
#endif  
  
    dbus_connection_unref(dbconn);
    return 0;
}

    The two codes are consistent,  client could send data to server. Hope it is useful.

Note the line in server.c:


 if(FALSE == dbus_message_get_args(message, &dberr, DBUS_TYPE_ARRAY,
 DBUS_TYPE_BYTE, &pReadData, &len,  DBUS_TYPE_INVALID) && 0 != len)


    The type of the length variable should be int. It is, inside the Dbus library, the lengh pointer has been assume as a pointer to an integer (int*). If you put the other integer types as length,  The values in vicinity of the passed length may be marred,  it would let to crash.

   More detail please ref this post in stack overflow.

If you do not know how to rewrite Makefile for the 2 codes, you could refer to this post I wrote.