Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob

Creating a Bluetooth LE Peripheral using ModusToolbox #3

lock attach
Attachments are accessible only for community members.

Creating a Bluetooth LE Peripheral using ModusToolbox #3

markgsaunders
Employee
Employee
50 sign-ins 10 solutions authored 5 solutions authored

Did you get your kit to connect? Could you connect and reconnect? Was it cool? Are you ready to make your device actually DO SOMETHING? Good idea - let's go!

Well, hang on a second, before we add a FindMe profile to the device I have one final thought on connections... if you ever find that you cannot connect from CySmart until you reset the kit it is probably because you were not quick enough. The default length of time that the device will advertise rapidly is 30s. After that it advertises at a far slower rate for 150s until giving up entirely to save power. The expectation is that the device would implement a wakeup feature to restart advertising rather than just burning power advertising while nothing is listening. If this annoys you just go into the GAP Settings in the BLE Configurator and, in the Advertising settings section, increase the "Fast advertising timeout" value. The other solution to this problem is to be johnny on the spot and not wander off for a coffee and a donut every time you press the program button. Not judgin'. Just sayin'.

The FindMe profile is a Bluetooth SIG-defined application that is implemented by the Immediate Alert service. It's one of the simplest profiles and all it does is let a client send a single value to a GATT server. We are going to use it to light LEDs corresponding to the value sent from the central. Legal values are 0 (no alert), 1 (mild alert), and 2 (high alert). Did I mention that it is simple? All we need to do to make this work is to configure the GATT database with the service, write a handler for when an alert event happens (meaning you send a message from your phone), and a function to write messages and light LEDs, which we will call from the handler.

In the BLE Configurator, go to the GATT Settings tab. Notice that the GATT server already contains the mandatory Generic Access (GAP) , which contains the name you gave your device yesterday, and Generic Access services.

ModusToolbox BLE Configurator showing the GAP profile in the GATT database

Now let's add the service. You can do this in two ways - add the profile or just the service. For Find Me, which we are going to use, this is basically the same thing. But in more complex applications you might add an alert service to a different profile to increase it's functionality. The profile method is to select the "GATT" heading and click the green '+' button. From the menu choose "Find Me" and the "Find Me Target (GATT Server)". The easier way is to select "Server" instead of "GATT". Then choose "Immediate Alert" noting that the client or server choice is implicit. Do NOT choose the "Alert Notification" service. That is a far richer service, that you will be welcome to try once you have mastered the simple alert. Easy there, Tiger!!!

The service gets added to the GATT database and you can choose "No Alert" as the initial condition. It seems like a good idea to start the device in a nice relaxed state, doesn't it? Note that the service enables write but not read - it will not let the phone ask for the level.

ModusToolbox BLE Configurator showing the Find Me profile in the GATT database

Let's save those edits and write a little code in main.c. Let's start with a function to set the LEDs based on the alert level.

void alert( uint8_t level )
{
  Cy_SCB_UART_PutString( KIT_UART_HW, "Alert " );

  switch( level )
  {
    case CY_BLE_NO_ALERT: /* Both LEDs off */
      Cy_SCB_UART_PutString( KIT_UART_HW, "OFF\r\n" );
      Cy_GPIO_Set( KIT_LED1_PORT, KIT_LED1_PIN );
      Cy_GPIO_Set( KIT_LED2_PORT, KIT_LED2_PIN );
    break;

    case CY_BLE_MILD_ALERT: /* LED1 off, LED2 on */
      Cy_SCB_UART_PutString( KIT_UART_HW, "MILD\r\n" );
      Cy_GPIO_Set( KIT_LED1_PORT, KIT_LED1_PIN );
      Cy_GPIO_Clr( KIT_LED2_PORT, KIT_LED2_PIN );
    break;

    case CY_BLE_HIGH_ALERT: /* LED1 on, LED2 off */
      Cy_SCB_UART_PutString( KIT_UART_HW, "HIGH\r\n" );
      Cy_GPIO_Clr( KIT_LED1_PORT, KIT_LED1_PIN );
      Cy_GPIO_Set( KIT_LED2_PORT, KIT_LED2_PIN );
    break;

    default: /* Both LEDs on - should never occur */
      Cy_SCB_UART_PutString( KIT_UART_HW, "ERROR\r\n" );
      Cy_GPIO_Clr( KIT_LED1_PORT, KIT_LED1_PIN );
      Cy_GPIO_Clr( KIT_LED2_PORT, KIT_LED2_PIN );
    break;
  }
}

OK, now we need a handler. Here is the code for that. Note that, like stack_handler(), it takes an event and a parameter argument. Our code checks the event is a write to the alert characteristic so we do not do something silly if the phone is just reading the value (although we did not give it read access anyway - this is belt-and-braces safe programming). It then extracts the value into a local variable and calls the alert() function. Note that you do not need to write the value into the database - that has already been done for you by the stack.

void IAS_handler( uint32_t event, void* eventParam )
{
  uint8_t alertLevel;

  /* Alert Level Characteristic write event */
  if( event == CY_BLE_EVT_IASS_WRITE_CHAR_CMD )
  {
    /* Read the updated Alert Level value from the GATT database */
    Cy_BLE_IASS_GetCharacteristicValue( CY_BLE_IAS_ALERT_LEVEL, sizeof( alertLevel ), &alertLevel );

    alert( alertLevel );
  }
}

We are nearly done now. We have to register the handler with the stack - just like we did with stack_handler() - and that is done in main().

Cy_BLE_RegisterEventCallback( stack_handler );

Cy_BLE_IAS_RegisterAttrCallback( IAS_handler );

 

Finally, we should make sure that the alert does not out-live the connection. Call the alert() function on a disconnect event in stack_handler(), like this.

 

case CY_BLE_EVT_GAP_DEVICE_DISCONNECTED:
   Cy_SCB_UART_PutString( KIT_UART_HW, "Disconnected\r\n" );
   Cy_BLE_GAPP_StartAdvertisement( CY_BLE_ADVERTISING_FAST, CY_BLE_PERIPHERAL_CONFIGURATION_0_INDEX );

   alert( CY_BLE_NO_ALERT );
break;

Program that into the device and connect via CySmart. Instead of "No Services" you will see the GATT database (which is a way to access all services) and the Find Me profile (which presents a profile-specific interface.

CySmart profiles

Swipe to the Find Me profile and use the button to change the alert level.

CySmart Find Me profile interface

It should change the state of the LEDs and print useful messages to the terminal. Notice how the alert gets set to OFF when I disconnected.

Terminal emulator output

I have attached the C code again (as a zip) to help you cut-and-paste into your own project. Next time, we'll add a battery service, which will send messages in the other direction.. letting the phone know that the state of the peripheral has changed.

465 Views
Authors