You are here

Creating a Bluetooth LE Peripheral using ModusToolbox #4 | 赛普拉斯半导体

Creating a Bluetooth LE Peripheral using ModusToolbox #4

If you followed along with my last blog, you should have a working BLE peripheral today. You should be able to send commands from your phone to the peripheral with wild abandon. It's good to be the King! Today we are going to turn things around and send messages from the peripheral to the phone.

Before we jump in, though, you may have noticed that the CySmart App has a tendency to remember a little too much about the connected peripheral. You program in a new application but the app keeps showing the old configuration. For example, you might have struggled to get the FindMe profile to appear when you connect. In today's example we are going to add another profile so it's a good time to give you a "clear your cache" tip. CySmart remembers the peripheral name and ID after a connection. As a result it thinks it knows that the device can do... but it has changed. To force it to refresh, go to your BLE settings on the phone and clear/delete the device. If the device is not there (exact behavior depends upon the phone and OS version) just give BLE the good old off-n-on treatment. On my phone I just swipe the wee green slider widget left, and then right again, and I am good to go.

OK, so let's add the Battery service to our phone. Things start out the same way as they did yesterday. Open the Configurator and add a "Battery" service in the "Server", alongside the Immediate Alert. Then click on the "Battery Level" characteristic and make a couple of edits. For value, write "100", which corresponds to a full battery (it is a percentage). The check the "Notify" box. That last edit is the cunning part...

ModusToolbox BLE Configurator setting up a Battery service

Save your changes and go back to editing main.c. The reason why this checkbox is important is that it enables the peripheral to update the phone without the phone expressly asking for the battery level. Woooo-hoooo! We are going to write a few lines of code that run when you press the button on the kit. That code will check that there is a connection, read the current battery level, decrement it, write the value back to the GATT database, and then tell the phone what happened.

Start by creating a global to hold the connection information. We need this because BLE supports multiple connections and it is important to only send messages to the right phone. When the transferred data is bit more important that the battery level, this becomes a big deal!

static cy_stc_ble_conn_handle_t conn_handle;

Next, we need to set the variable on a connection and clear it upon disconnect. Yes, you are right, I am being a little bit lazy by not maintaining an array of handles. But my application only supports one connection at a time. And I am really lazy person. So deal with it! Here's the code in stack_handler() - notice how the eventParam for the connection event is the handle itself.

 case CY_BLE_EVT_GATT_CONNECT_IND:
   Cy_SCB_UART_PutString( KIT_UART_HW, "Connected\r\n" );
   conn_handle = *(cy_stc_ble_conn_handle_t *)eventParam;
 break;

 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 );
   conn_handle.attId = 0;
   conn_handle.bdHandle = 0;
 break;

Just like the Immediate Alert, we should have an attribute change handler. All mine does is print the state of the notification attribute.

void BAS_handler( uint32_t event, void* eventParam )
{
 Cy_SCB_UART_PutString( KIT_UART_HW, "Notification " );
 Cy_SCB_UART_PutString( KIT_UART_HW, ( CY_BLE_EVT_BASS_NOTIFICATION_ENABLED == event ) ? "on\r\n" : "off\r\n" );
}

 

Register the handler in the same way as before, in main().

 Cy_BLE_RegisterEventCallback( stack_handler );
 Cy_BLE_IAS_RegisterAttrCallback( IAS_handler );
 Cy_BLE_BAS_RegisterAttrCallback( BAS_handler );

 

One last thing before playtime. Update the button_isr() function to update the GATT and notify the phone.

  1. Check for a valid connection with Cy_BLE_GetConnectionState()
  2. Read the current battery level from GATT with Cy_BLE_BASS_GetCharacteristicValue() - note that the function takes a size and a pointer argument for the data to be read
  3. Simulate a depleting battery by decrementing the level (and wrap back to 100 should you press the button 100 times)
  4. Write the new level to the database
  5. Read the notify characteristic from GATT with Cy_BLE_BASS_SetCharacteristicValue()
  6. If it is set then push a message up to the phone with Cy_BLE_BASS_SendNotification()

Here is the code to do that. Add it after you clear the interrupt. It's a little daunting the first time but I think that the function names become obvious pretty quickly and you soon get the hang of the defines for the service index and characteristics.

 if( Cy_BLE_GetConnectionState( conn_handle ) == CY_BLE_CONN_STATE_CONNECTED )
 {
   uint8_t battery_level;
   uint8_t notify;

   /* Read the current battery level from GATT */
   Cy_BLE_BASS_GetCharacteristicValue( CY_BLE_BATTERY_SERVICE_INDEX, CY_BLE_BAS_BATTERY_LEVEL, sizeof( battery_level ), &battery_level );

   /* Decrement the level */
   battery_level--;
   if( battery_level == (uint8_t)(-1) )
     battery_level = 100;

   /* Write the new level back to GATT */
   Cy_BLE_BASS_SetCharacteristicValue( CY_BLE_BATTERY_SERVICE_INDEX, CY_BLE_BAS_BATTERY_LEVEL, sizeof( battery_level ), &battery_level );

   /* Is notification enabled? If yes, tell the central */
   Cy_BLE_BASS_GetCharacteristicDescriptor( conn_handle, CY_BLE_BATTERY_SERVICE_INDEX, CY_BLE_BAS_BATTERY_LEVEL, CY_BLE_BAS_BATTERY_LEVEL_CCCD, sizeof( notify ), &notify );
   if( notify )
     Cy_BLE_BASS_SendNotification( conn_handle, CY_BLE_BATTERY_SERVICE_INDEX, CY_BLE_BAS_BATTERY_LEVEL, sizeof( battery_level ), &battery_level );
 }


Done typing? Good, program that bad boy!

When you connect now, you should see a third profile - Battery Service. It shows a full battery (because we set the value to 100 in the Configurator). And there are two buttons - Read and Notify. If you press Read it looks like nothing happens because it is just reading the same value every time. Press the button on the kit and read again... it should come back with 99. Now press the notify button and repeatedly hit that kit button. Watch as the battery level steadily decreases... you can go all the way down to zero and wrap around if you wish. In fact I know you will. You cannot stop yourself!

Well done - you have now created a peripheral device that can send and receive messages. Pretty cool no? But there is the slight problem that any Tom, Disk or Harry can make a connection. Tomorrow, I'll wrap up this series of blogs by adding a passkey to control the pairing of phone and device. As before, the updated main.c file is attached.

Blog: 

本网站上的所有内容和材料均“按原样”提供。赛普拉斯半导体公司及其各个供应商对这些材料用于任何用途的适用性不作陈述,并且对关于这些材料的所有担保和条件概不负责,包括但不限于有关适销性、针对特定用途之适用性、权利和不侵犯任何第三方知识产权的所有暗示担保和条件。赛普拉斯半导体公司不授予任何明示或暗示的许可(无论是以默许方式或是任何其他方式)。使用本网站上的信息可能需要第三方的许可,或赛普拉斯半导体公司的许可。

本网站上的内容可能包含或必须遵守关于使用的特定准则或限制。所有帖子和使用本网站上的内容都必须遵守本网站的条款与条件;使用这些内容的第三方必须同意遵守任何限制或准则,并遵守本网站的条款与条件。赛普拉斯半导体公司及其供应商保留随时对内容和材料、产品、计划和服务进行纠正、删除、修改、增强、改进或其他变更,或者移动或终止任何内容、产品、计划或服务的权利,恕不另行通知。