Summary
This article is a discussion of a library of utilities functions that support AnyCloud Bluetooth development. It includes settings to configure the hardware, functions to decode stack events, functions to decode advertising packets etc.
Story
The Cypress, now Infineon, Modus Toolbox team has been working to bring the WICED Bluetooth devices to be closer and more compatible with the PSoC 6 tools. One of the important enablement things we have done is turn on the WICED Bluetooth Host stack on the PSoC 6 so that it can effectively talk to the CYW43XXX Bluetooth / WiFi combo chips.
As I have been using all of the new stuff I found myself adding my own custom functionality…. and after a while I realized (finally) that I should put all of those little helper things into a library, specifically an IoT Expert library. For now this library contains:
- The Bluetooth Platform Configuration Settings
- Functions to decode stack events
- Functions to decode advertising packets
but imagine with time I will add more functions and templates.
bt_platform_cfg_settings
As I discussed in the article AnyCloud Bluetooth Advertising Scanner (Part 1), every single AnyCloud BLE Stack project will require a structure of type wiced_bt_cfg_settings_t. This structure is used to setup the hardware before getting the stack going. Remember you need to make a call like this to initialize the Bluetooth hardware.
cybt_platform_config_init(&bt_platform_cfg_settings);
At some point this file will be included automatically for you by the Modus Toolbox team, but for now I have added this to my library. This file look like this. Basically a bunch of pin definitions which are set for you automatically by the BSP. Obviously you can make your own, but this should work on all of the Cypress development kits (I think)
#include "cybt_platform_config.h" #include "cybsp.h" #include "wiced_bt_stack.h" const cybt_platform_config_t bt_platform_cfg_settings = { .hci_config = { .hci_transport = CYBT_HCI_UART, .hci = { .hci_uart = { .uart_tx_pin = CYBSP_BT_UART_TX, .uart_rx_pin = CYBSP_BT_UART_RX, .uart_rts_pin = CYBSP_BT_UART_RTS, .uart_cts_pin = CYBSP_BT_UART_CTS, .baud_rate_for_fw_download = 115200, .baud_rate_for_feature = 115200, .data_bits = 8, .stop_bits = 1, .parity = CYHAL_UART_PARITY_NONE, .flow_control = WICED_TRUE } } }, .controller_config = { .bt_power_pin = CYBSP_BT_POWER, .sleep_mode = { #if (bt_0_power_0_ENABLED == 1) /* BT Power control is enabled in the LPA */ #if (CYCFG_BT_LP_ENABLED == 1) /* Low power is enabled in the LPA, use the LPA configuration */ .sleep_mode_enabled = true, .device_wakeup_pin = CYCFG_BT_DEV_WAKE_GPIO, .host_wakeup_pin = CYCFG_BT_HOST_WAKE_GPIO, .device_wake_polarity = CYCFG_BT_DEV_WAKE_POLARITY, .host_wake_polarity = CYCFG_BT_HOST_WAKE_IRQ_EVENT #else /* Low power is disabled in the LPA, disable low power */ .sleep_mode_enabled = false #endif #else /* BT Power control is disabled in the LPA – default to BSP low power configuration */ .sleep_mode_enabled = true, .device_wakeup_pin = CYBSP_BT_DEVICE_WAKE, .host_wakeup_pin = CYBSP_BT_HOST_WAKE, .device_wake_polarity = CYBT_WAKE_ACTIVE_LOW, .host_wake_polarity = CYBT_WAKE_ACTIVE_LOW #endif } }, .task_mem_pool_size = 2048 };
btutil_stack
When you startup the Bluetooth Host stack you are responsible for providing a “management callback” which has the function prototype
typedef wiced_result_t (wiced_bt_management_cback_t) (wiced_bt_management_evt_t event, wiced_bt_management_evt_data_t *p_event_data);
The first parameter is an “event” which is just an enumerated list of possible events by the Bluetooth Host Stack. Here is the actual list.
enum wiced_bt_management_evt_e { /* Bluetooth status events */ BTM_ENABLED_EVT, /**< Bluetooth controller and host stack enabled. Event data: wiced_bt_dev_enabled_t */ BTM_DISABLED_EVT, /**< Bluetooth controller and host stack disabled. Event data: NULL */ BTM_POWER_MANAGEMENT_STATUS_EVT, /**< Power management status change. Event data: wiced_bt_power_mgmt_notification_t */ BTM_RE_START_EVT, /**< Bluetooth controller and host stack re-enabled. Event data: tBTM_ENABLED_EVT */ /* Security events */ BTM_PIN_REQUEST_EVT, /**< PIN request (used only with legacy devices). Event data: #wiced_bt_dev_name_and_class_t */ BTM_USER_CONFIRMATION_REQUEST_EVT, /**< received USER_CONFIRMATION_REQUEST event (respond using #wiced_bt_dev_confirm_req_reply). Event data: #wiced_bt_dev_user_cfm_req_t */ BTM_PASSKEY_NOTIFICATION_EVT, /**< received USER_PASSKEY_NOTIFY event. Event data: #wiced_bt_dev_user_key_notif_t */ BTM_PASSKEY_REQUEST_EVT, /**< received USER_PASSKEY_REQUEST event @cond DUAL_MODE (respond using #wiced_bt_dev_pass_key_req_reply). Event data: #wiced_bt_dev_user_key_req_t @endcond @note BR/EDR Only */ BTM_KEYPRESS_NOTIFICATION_EVT, /**< received KEYPRESS_NOTIFY event. Event data: #wiced_bt_dev_user_keypress_t */ BTM_PAIRING_IO_CAPABILITIES_BR_EDR_REQUEST_EVT, /**< Requesting IO capabilities for BR/EDR pairing. Event data: #wiced_bt_dev_bredr_io_caps_req_t @note BR/EDR Only */ BTM_PAIRING_IO_CAPABILITIES_BR_EDR_RESPONSE_EVT,/**< Received IO capabilities response for BR/EDR pairing. Event data: @cond DUAL_MODE #wiced_bt_dev_bredr_io_caps_rsp_t @endcond @note BR/EDR Only*/ BTM_PAIRING_IO_CAPABILITIES_BLE_REQUEST_EVT, /**< Requesting IO capabilities for BLE pairing. Slave can check peer io capabilities in event data before updating with local io capabilities. Event data: #wiced_bt_dev_ble_io_caps_req_t */ BTM_PAIRING_COMPLETE_EVT, /**< received SIMPLE_PAIRING_COMPLETE event. Event data: #wiced_bt_dev_pairing_cplt_t */ BTM_ENCRYPTION_STATUS_EVT, /**< Encryption status change. Event data: #wiced_bt_dev_encryption_status_t */ BTM_SECURITY_REQUEST_EVT, /**< Security request (respond using #wiced_bt_ble_security_grant). Event data: #wiced_bt_dev_security_request_t */ BTM_SECURITY_FAILED_EVT, /**< Security procedure/authentication failed. Event data: #wiced_bt_dev_security_failed_t */ BTM_SECURITY_ABORTED_EVT, /**< Security procedure aborted locally, or unexpected link drop. Event data: #wiced_bt_dev_name_and_class_t */ BTM_READ_LOCAL_OOB_DATA_COMPLETE_EVT, /**< Result of reading local OOB data @cond DUAL_MODE (#wiced_bt_dev_read_local_oob_data). Event data: #wiced_bt_dev_local_oob_t @endcond @note BR/EDR Only */ BTM_REMOTE_OOB_DATA_REQUEST_EVT, /**< OOB data from remote device @cond DUAL_MODE (respond using #wiced_bt_dev_remote_oob_data_reply). Event data: #wiced_bt_dev_remote_oob_t @endcond @note BR/EDR Only */ BTM_PAIRED_DEVICE_LINK_KEYS_UPDATE_EVT, /**< Updated remote device link keys (store device_link_keys to NV memory). This is the place to verify that the correct link key has been generated. Event data: #wiced_bt_device_link_keys_t */ BTM_PAIRED_DEVICE_LINK_KEYS_REQUEST_EVT, /**< Request for stored remote device link keys (restore device_link_keys from NV memory). If successful, return WICED_BT_SUCCESS. Event data: #wiced_bt_device_link_keys_t */ BTM_LOCAL_IDENTITY_KEYS_UPDATE_EVT, /**< Update local identity key (stored local_identity_keys NV memory). Event data: #wiced_bt_local_identity_keys_t */ BTM_LOCAL_IDENTITY_KEYS_REQUEST_EVT, /**< Request local identity key (get local_identity_keys from NV memory). If successful, return WICED_BT_SUCCESS. Event data: #wiced_bt_local_identity_keys_t */ BTM_BLE_SCAN_STATE_CHANGED_EVT, /**< BLE scan state change. Event data: #wiced_bt_ble_scan_type_t */ BTM_BLE_ADVERT_STATE_CHANGED_EVT, /**< BLE advertisement state change. Event data: #wiced_bt_ble_advert_mode_t */ /* BLE Secure Connection events */ BTM_SMP_REMOTE_OOB_DATA_REQUEST_EVT, /**< SMP remote oob data request. Reply using wiced_bt_smp_oob_data_reply. Event data: #wiced_bt_smp_remote_oob_req_t */ BTM_SMP_SC_REMOTE_OOB_DATA_REQUEST_EVT, /**< LE secure connection remote oob data request. Reply using wiced_bt_smp_sc_oob_reply. Event data: #wiced_bt_smp_sc_remote_oob_req_t @note BR/EDR Only */ BTM_SMP_SC_LOCAL_OOB_DATA_NOTIFICATION_EVT, /**< LE secure connection local OOB data (wiced_bt_smp_create_local_sc_oob_data). Event data: #wiced_bt_smp_sc_local_oob_t */ BTM_SCO_CONNECTED_EVT, /**< SCO connected event. Event data: @cond DUAL_MODE #wiced_bt_sco_connected_t @endcond @note BR/EDR Only */ BTM_SCO_DISCONNECTED_EVT, /**< SCO disconnected event. Event data: @cond #wiced_bt_sco_disconnected_t @endcond @note BR/EDR Only */ BTM_SCO_CONNECTION_REQUEST_EVT, /**< SCO connection request event. Event data: @cond #wiced_bt_sco_connection_request_t @endcond @note BR/EDR Only */ BTM_SCO_CONNECTION_CHANGE_EVT, /**< SCO connection change event. Event data: @cond #wiced_bt_sco_connection_change_t @endcond @note BR/EDR Only */ BTM_BLE_CONNECTION_PARAM_UPDATE, /**< BLE connection parameter update. Event data: #wiced_bt_ble_connection_param_update_t */ BTM_BLE_PHY_UPDATE_EVT, /**< BLE Physical link update. Event data: wiced_bt_ble_phy_update_t */ BTM_LPM_STATE_LOW_POWER, /**< BT device wake has been deasserted. Used for Host Stack Use Case. */ BTM_MULTI_ADVERT_RESP_EVENT, /**< Multi adv command status event Used for the status of the command sent */ #if SMP_CATB_CONFORMANCE_TESTER == TRUE BTM_SMP_SC_PEER_INFO_EVT /** The Secure Connections support information of the peer device */ #endif };
While you are trying to figure out what is going on during the development, it is very useful to be able to print out the name of the events (instead of the numbers). In other words instead of doing
printf("Event = %02X\n",event);
it is way better to do
printf("Event Name=%s\n",btutil_getBTEventName(event));
While I was looking at an example project I found this function which I thought was awesome.
const char *btutil_getBTEventName(wiced_bt_management_evt_t event) { switch ( (int)event ) { CASE_RETURN_STR(BTM_ENABLED_EVT) CASE_RETURN_STR(BTM_DISABLED_EVT) CASE_RETURN_STR(BTM_POWER_MANAGEMENT_STATUS_EVT) CASE_RETURN_STR(BTM_PIN_REQUEST_EVT) CASE_RETURN_STR(BTM_USER_CONFIRMATION_REQUEST_EVT) CASE_RETURN_STR(BTM_PASSKEY_NOTIFICATION_EVT) CASE_RETURN_STR(BTM_PASSKEY_REQUEST_EVT) CASE_RETURN_STR(BTM_KEYPRESS_NOTIFICATION_EVT) CASE_RETURN_STR(BTM_PAIRING_IO_CAPABILITIES_BR_EDR_REQUEST_EVT) CASE_RETURN_STR(BTM_PAIRING_IO_CAPABILITIES_BR_EDR_RESPONSE_EVT) CASE_RETURN_STR(BTM_PAIRING_IO_CAPABILITIES_BLE_REQUEST_EVT) CASE_RETURN_STR(BTM_PAIRING_COMPLETE_EVT) CASE_RETURN_STR(BTM_ENCRYPTION_STATUS_EVT) CASE_RETURN_STR(BTM_SECURITY_REQUEST_EVT) CASE_RETURN_STR(BTM_SECURITY_FAILED_EVT) CASE_RETURN_STR(BTM_SECURITY_ABORTED_EVT) CASE_RETURN_STR(BTM_READ_LOCAL_OOB_DATA_COMPLETE_EVT) CASE_RETURN_STR(BTM_REMOTE_OOB_DATA_REQUEST_EVT) CASE_RETURN_STR(BTM_PAIRED_DEVICE_LINK_KEYS_UPDATE_EVT) CASE_RETURN_STR(BTM_PAIRED_DEVICE_LINK_KEYS_REQUEST_EVT) CASE_RETURN_STR(BTM_LOCAL_IDENTITY_KEYS_UPDATE_EVT) CASE_RETURN_STR(BTM_LOCAL_IDENTITY_KEYS_REQUEST_EVT) CASE_RETURN_STR(BTM_BLE_SCAN_STATE_CHANGED_EVT) CASE_RETURN_STR(BTM_BLE_ADVERT_STATE_CHANGED_EVT) CASE_RETURN_STR(BTM_SMP_REMOTE_OOB_DATA_REQUEST_EVT) CASE_RETURN_STR(BTM_SMP_SC_REMOTE_OOB_DATA_REQUEST_EVT) CASE_RETURN_STR(BTM_SMP_SC_LOCAL_OOB_DATA_NOTIFICATION_EVT) CASE_RETURN_STR(BTM_SCO_CONNECTED_EVT) CASE_RETURN_STR(BTM_SCO_DISCONNECTED_EVT) CASE_RETURN_STR(BTM_SCO_CONNECTION_REQUEST_EVT) CASE_RETURN_STR(BTM_SCO_CONNECTION_CHANGE_EVT) CASE_RETURN_STR(BTM_BLE_CONNECTION_PARAM_UPDATE) #ifdef CYW20819A1 CASE_RETURN_STR(BTM_BLE_PHY_UPDATE_EVT) #endif } return NULL; }
And then I learned something new when I looked at the “function” CASE_RETURN_STR which used compiler trick to turn an enumerated value into a string.
#define CASE_RETURN_STR(enum_val) case enum_val: return #enum_val;
This allows you to do this in your management callback (notice line 16 where the string is printed out)
wiced_result_t app_bt_management_callback(wiced_bt_management_evt_t event, wiced_bt_management_evt_data_t *p_event_data) { wiced_result_t result = WICED_BT_SUCCESS; switch (event) { case BTM_ENABLED_EVT: if (WICED_BT_SUCCESS == p_event_data->enabled.status) { printf("Started BT Stack Succesfully\n"); wiced_bt_ble_observe(WICED_TRUE,0,obv_callback); } break; default: printf("Unhandled Bluetooth Management Event: %s\n", btutil_getBTEventName(event)); break; } return result; }
It turns out that there are several other places where the stack gives you an event-ish thing. So these functions were created as well
#pragma once const char *btutil_getBTEventName(wiced_bt_management_evt_t event); const char *btutil_getBLEAdvertModeName(wiced_bt_ble_advert_mode_t mode); const char *btutil_getBLEGattDisconnReasonName(wiced_bt_gatt_disconn_reason_t reason); const char *btutil_getBLEGattStatusName(wiced_bt_gatt_status_t status);
btutil_adv_decode
In AnyCloud Bluetooth Advertising Scanner (Part 3) I discussed the format of the advertising packet. So I created functions which will decode the data in the advertising packets. More on the future article AnyCloud Bluetooth Advertising Scanner (Part 4 or maybe 5 or maybe 6). Here are the functions:
#pragma once wiced_bool_t btutil_isEddystone(uint8_t *data); wiced_bool_t btutil_is_iBeacon(uint8_t *data); wiced_bool_t btutil_isCypress(uint8_t *data); int btutil_adv_len(uint8_t *packet); void btutil_adv_printPacketDecode(uint8_t *packet); void btutil_adv_printPacketBytes(uint8_t *packet);
btutil
And because I like to have just one include to get access to all of the function I created “btutil.h” which just includes all of the headers in one place.
#pragma once #include "btutil_adv_decode.h" #include "btutil_stack.h" #include "btutil_general.h" #include "bt_platform_cfg_settings.h"
Add to the IoT Expert Manifest
In order to set it up for my library to live in the library manager I updated the IoT Expert Manifest file to have a link to the GitHub repository
<middleware> <name>Bluetooth Utilities</name> <id>btutil</id> <uri>https://github.com/iotexpert/btutil</uri> <desc>A library of Bluetooth Debugging Utilties for Cypress PSoC6 Anycloud</desc> <category>IoT Expert</category> <req_capabilities>psoc6</req_capabilities> <versions> <version flow_version="1.0,2.0"> <num>master</num> <commit>master</commit> <desc>master</desc> </version> </versions> </middleware>
And now the library manager has the IoT Expert Bluetooth Utilities.