2017年8月30日 星期三

nRF5x: change advertising data manually without re-start

unlike other BLE MCU, nRF5X is able to change advertising data without restart .
the example shows how to easily change BLE peripheral advertising data, so the central can watch status of peripheral without connect it.


*
about how to start UART printf
please read this article:
http://gaiger-programming.blogspot.tw/2016/12/nrf51-make-printf-works-well.html


STEP 1. make advertising non-stop
open "ble_app_template" form:
\nRF5x_SDK_11.0.0\examples\ble_peripheral\ble_app_template

change ble device name at about line 63

#define DEVICE_NAME                      "advertising test"  

change APP_ADV_TIMEOUT_IN_SECONDS  at about line 65

#define APP_ADV_TIMEOUT_IN_SECONDS       0                                        

and in advertising_init( ) function
change advdata.flags

    uint32_t      err_code;
    ble_advdata_t advdata;
    
    memset(&advdata, 0, sizeof(advdata));

    advdata.name_type               = BLE_ADVDATA_FULL_NAME;
    advdata.include_appearance      = true;
    advdata.flags                   = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;
    advdata.uuids_complete.uuid_cnt = sizeof(m_adv_uuids) / sizeof(m_adv_uuids[0]);
    advdata.uuids_complete.p_uuids  = m_adv_uuids;

    ble_adv_modes_config_t options = {0};
    options.ble_adv_fast_enabled  = BLE_ADV_FAST_ENABLED;
    options.ble_adv_fast_interval = APP_ADV_INTERVAL;
    options.ble_adv_fast_timeout  = APP_ADV_TIMEOUT_IN_SECONDS;
    err_code = ble_advertising_init(&advdata, NULL, &options, on_adv_evt, NULL);
  
	
    APP_ERROR_CHECK(err_code);                                       

re-build all and load to your board , you can see its advertising never-stop,
and the adverting raw data will be like:



"6164766572746973696e672074657374" in hex are "advertising test" in string.


STEP 2. add a repeated timer to change advertising data manually

add this definition at about line 95

APP_TIMER_DEF(m_adv_name_change_timer_id);
#define ADV_NAME_CHANGE_INTERVAL            APP_TIMER_TICKS(1*1000, APP_TIMER_PRESCALER)
I set the timer repeated in 1 second interval.

and create timer in timers_init( )


uint32_t err_code;
err_code = app_timer_create(&m_adv_name_change_timer_id,
                            APP_TIMER_MODE_REPEATED,
                            adv_name_change_timeout_handler);

APP_ERROR_CHECK(err_code);

add  adv_name_change_timeout_handler( )

#define DEVICE_NAME_1 "0987654321"
#define DEVICE_NAME_2 "1234567890"
static uint8_t index = 0;
static void adv_name_change_timeout_handler(void * p_context)
{
	UNUSED_PARAMETER(p_context);	
	uint32_t err_code;
	
	printf("change adv name\r\n");	

	ble_gap_conn_sec_mode_t sec_mode;
	BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode);
	
	
	if (0==index)
	{
		err_code = sd_ble_gap_device_name_set(&sec_mode,
                 (const uint8_t *)DEVICE_NAME_1,strlen(DEVICE_NAME_1));

		APP_ERROR_CHECK(err_code);
		index = 1;																		
	}
	else if (1==index)
	{
		err_code = sd_ble_gap_device_name_set(&sec_mode,
                 (const uint8_t *)DEVICE_NAME_2,	strlen(DEVICE_NAME_2));

		APP_ERROR_CHECK(err_code);
		index = 0;								
	}
	
	
	ble_advdata_t advdata;
	memset(&advdata, 0, sizeof(advdata));

	advdata.name_type               = BLE_ADVDATA_FULL_NAME;
	advdata.include_appearance      = true;
	advdata.flags                   = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;
	advdata.uuids_complete.uuid_cnt = sizeof(m_adv_uuids) / sizeof(m_adv_uuids[0]);
	advdata.uuids_complete.p_uuids  = m_adv_uuids;
																				
	err_code = ble_advdata_set(&advdata,NULL);																			
	APP_ERROR_CHECK(err_code);
	
}


start the timer at application_timers_start( )

uint32_t err_code;
	
err_code = app_timer_start(m_adv_name_change_timer_id, ADV_NAME_CHANGE_INTERVAL, NULL);	
APP_ERROR_CHECK(err_code);

press rebuild-all and load to your board



STEP 3: check advertising data of your device
the adverting raw data will change from



to


in 1 sec interval.


note:
1. in nrf_connect app you can see more detail about the advertising raw data fields:



2. the max length of device name is 29 byte, which can have many usage.
reference : https://devzone.nordicsemi.com/question/121842/max-length-of-the-device-name-in-nrf52832/































2017年8月29日 星期二

nRF51: how to simply port nRF51822 code to nRF51802

For some reasons you might need nRF51802 instead of nRF51822: hardware I2C, more PWM channel ...This article shows how to simply port the code developed on nRF51822 to  nRF51802.

nRF51802 MUST use SDK11 or LATER.

STEP 1. open "pca10028.h"

about line.150 , there are code like this:

#define NRF_CLOCK_LFCLKSRC      {.source        = NRF_CLOCK_LF_SRC_XTAL,            \
                                 .rc_ctiv       = 0,                                \
                                 .rc_temp_ctiv  = 0,                                \
                                 .xtal_accuracy = NRF_CLOCK_LF_XTAL_ACCURACY_20_PPM}

change like this :

/*power by AgathaKuan*/
#ifndef NRF51802
#define NRF_CLOCK_LFCLKSRC      {.source        = NRF_CLOCK_LF_SRC_XTAL,            \
                                 .rc_ctiv       = 0,                                \
                                 .rc_temp_ctiv  = 0,                                \
                                 .xtal_accuracy = NRF_CLOCK_LF_XTAL_ACCURACY_20_PPM}
#else
#define NRF_CLOCK_LFCLKSRC      {.source        = NRF_CLOCK_LF_SRC_SYNTH,            \
                                 .rc_ctiv       = 0,                                \
                                 .rc_temp_ctiv  = 0,                                \
                                 .xtal_accuracy = NRF_CLOCK_LF_XTAL_ACCURACY_20_PPM}
#endif

STEP 2. check your UART pins
at about line 65

#define RX_PIN_NUMBER  11
#define TX_PIN_NUMBER  9

but nRF51802 DEFAULT UART pins are P0_1 &P0_4
(as nRF51822  TWI pins)

so, change as

#ifndef NRF51802
#define RX_PIN_NUMBER  11
#define TX_PIN_NUMBER  9
#else

#define RX_PIN_NUMBER  1
#define TX_PIN_NUMBER  4
#endif

(The UART pin should be refered to your circuit diagram, please check it or ask your circuit engineer)


STEP 3. add nrf51802 definition
add this at about line 60 in "pca10028"

#define NRF51802


STEP 4. press to "re-built all"

press the button to re-build all , than load it into nRF51802











Addition:
1. if you also need to use TWI pins, open "twi_master_config.h" and change the following definition
as your specified pins.
#ifndef TWI_MASTER_CONFIG
#define TWI_MASTER_CONFIG

/*agatha: change the following definition to pin numbers you want*/
#define TWI_MASTER_CONFIG_CLOCK_PIN_NUMBER (YOUR_SCL_PIN)
#define TWI_MASTER_CONFIG_DATA_PIN_NUMBER (YOUR_SDA_PIN) 


#endif







2017年8月20日 星期日

nRF5x : how to control 4 channel pwm in real-time with BLE

This article is to help someone who wants to manipulate 4 channels PWM without lossing BLE.


The PWM example in nRF5x  SDK
(located in nRF5x_SDK_11.0.0\examples\peripheral\pwm_library)
has 2 insufficiency that if you follow SDK example totally:
1.the nRF51 is only able to control two channel PWM  with BLE, or the BLE and PWM would be disorderd.
2.the example is not enable to  start  BLE at the same time

Board : nRF51822 / pca10028

the following example is how to control 4 channel PWM with UART and enable BLE

STEP 0.  initial UART

Please follow this article:
http://gaiger-programming.blogspot.tw/2016/12/nrf51-make-printf-works-well.html?m=0


STEP 1. initial PWM
add this in Keil project setting



























and include these files


add this in main.c

#include "app_pwm.h"

and change your BLE device name

#define DEVICE_NAME                      "PWM Peripheral"


STEP 3. enable PWM timers

one timer can only use 2 channel PWM as the SDK's restriction , besides,  TIMER0 has been used by Softdevice to maintain scheduler.

so you have to enable TIMER1 and TIMER2
if  I use TIMER0, the Softdevice would not work normally.

if the PWM channel number is over 2,  the BLE and PWM would be disorderd

in "nrf_drv_config.h"

you will see about line 60

#define TIMER1_ENABLED 0

and about line 70

#define TIMER2_ENABLED 0

change as

//for pwm
//#define TIMER1_ENABLED 0 
#define TIMER1_ENABLED 1

//#define TIMER2_ENABLED 0
#define TIMER2_ENABLED 1


STEP 4. add PWM initial functions in main.c

about line 100

APP_PWM_INSTANCE(PWM1,1); 
APP_PWM_INSTANCE(PWM2,2);

APP_TIMER_DEF(m_pwm1_timer_id);
#define PWM1_TIMEOUT_INTERVAL     APP_TIMER_TICKS(50, APP_TIMER_PRESCALER)

APP_TIMER_DEF(m_pwm2_timer_id);
#define PWM2_TIMEOUT_INTERVAL     APP_TIMER_TICKS(50, APP_TIMER_PRESCALER)


static uint8_t led_1_value = 0;
static uint8_t led_2_value = 0;
static uint8_t led_3_value = 0;
static uint8_t led_4_value = 0;


static volatile bool ready_flag; 


void pwm_ready_callback(uint32_t pwm_id)    
{
    ready_flag = true;
}


in main function

app_pwm_config_t pwm1_cfg = APP_PWM_DEFAULT_CONFIG_2CH(2000L, LED_1, LED_2);
pwm1_cfg.pin_polarity[1] = APP_PWM_POLARITY_ACTIVE_LOW;
err_code = app_pwm_init(&PWM1,&pwm1_cfg,pwm_ready_callback);
APP_ERROR_CHECK(err_code);
app_pwm_enable(&PWM1);
  
app_pwm_config_t pwm2_cfg =  APP_PWM_DEFAULT_CONFIG_2CH(2000L, LED_3, LED_4);                  
pwm2_cfg.pin_polarity[0] = APP_PWM_POLARITY_ACTIVE_LOW;
err_code = app_pwm_init(&PWM2,&pwm2_cfg,pwm_ready_callback);
APP_ERROR_CHECK(err_code);
app_pwm_enable(&PWM2);



STEP 5. add app_timer for pwm



static void pwm1_timeout_handler(void * p_context)
{ 
    UNUSED_PARAMETER(p_context);

    ready_flag = false;
 
    while (app_pwm_channel_duty_set(&PWM1, 0, led_1_value) == NRF_ERROR_BUSY);  
    while (app_pwm_channel_duty_set(&PWM1, 1, led_2_value) == NRF_ERROR_BUSY); 
 
}


static void pwm2_timeout_handler(void * p_context)
{ 
    UNUSED_PARAMETER(p_context);

    ready_flag = false;
 
    while (app_pwm_channel_duty_set(&PWM2, 0, led_3_value) == NRF_ERROR_BUSY);  
    while (app_pwm_channel_duty_set(&PWM2, 1, led_4_value) == NRF_ERROR_BUSY); 
 
}

static void timers_init(void)
{
    // Initialize timer module.
    APP_TIMER_INIT(APP_TIMER_PRESCALER, APP_TIMER_OP_QUEUE_SIZE, false);

    /*agatha : add from here*/
    uint32_t err_code;
    err_code = app_timer_create(&m_pwm1_timer_id, 
                                APP_TIMER_MODE_SINGLE_SHOT, 
                                pwm1_timeout_handler);
    APP_ERROR_CHECK(err_code);
 
    err_code = app_timer_create(&m_pwm2_timer_id, 
                                APP_TIMER_MODE_SINGLE_SHOT, 
                                pwm2_timeout_handler);
    APP_ERROR_CHECK(err_code);
  
}


STEP 6. add command analyzed function in uart_event_handle

here is my command format (in hex)

0x40 0x01 value1 0x02 value2 0x03 value3 0x04 value4 0x23

for example,if I send :

40 01 ff 02 ff 03 ff 04 ff 23

than 4 channel PWM will be 100% (the max value of PWM)


add above uart_event_handle

static uint8_t  data_array[255];
static uint8_t  index;

and this is my uart_event_handle

void uart_event_handle(app_uart_evt_t * p_event)
{
    uint32_t        err_code; 
 
  
    switch (p_event->evt_type)
  {
   case APP_UART_DATA_READY:
    UNUSED_VARIABLE(app_uart_get(&data_array[index]));
    index++; 
   
    printf("uart event\r\n");
    if (((0x40 == data_array[0] && 0x23 == data_array[index-1]))
    { 
     
     for (uint8_t i = 0;i < index;i++)        
         app_uart_put(data_array[i]);
    
     led_1_value = data_array[2]*0x64/0xff;
     led_2_value = data_array[4]*0x64/0xff;
     
     err_code = app_timer_start(m_pwm1_timer_id,PWM1_TIMEOUT_INTERVAL,NULL);
     APP_ERROR_CHECK(err_code);
     
     led_3_value = data_array[6]*0x64/0xff;
     led_4_value = data_array[8]*0x64/0xff;
     
     err_code = app_timer_start(m_pwm2_timer_id,PWM2_TIMEOUT_INTERVAL,NULL);
     APP_ERROR_CHECK(err_code);
   
     memset(&data_array,0,sizeof(data_array));       
     index = 0;
    } 
    
    
    break;
   
   case APP_UART_COMMUNICATION_ERROR:
    APP_ERROR_HANDLER(p_event->data.error_communication);
    break;

   case APP_UART_FIFO_ERROR:
     APP_ERROR_HANDLER(p_event->data.error_code);
     break;

   default:
     break;
   
  } 
 
} 


STEP 7. test device BLE advertisement by your smart phone




STEP 8. Test PWM function

open your serial tool ( I use sscom)

first command I send in hex



and the second command



and you can see the different PWM of your LEDs


reference:

https://github.com/electronut/nRF51-RGB-LED-test