PSOC 4 BLE as Central/Client to read data from CapSense Proximity Example

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

cross mob
Anonymous
Not applicable

Hello All,

Have one PSOC 4 BLE programmed with the Pioneer kit CapSense Proximity Example acting as the Peripheral/Server.  Want to set up another to be the corresponding Central/Client.  Started from the Pioneer kit IAS Central Example and changed the Find Me Profile to Custom then duplicated the custom CapSense service in the BLE component.  Got the two to connect, now need to code the client to enable notifications on the server.  Been pouring over examples and reading the API but still very confused as to which API  command to use and what the arguments specifically need to be.  Specifically how to specify the custom CapSense service CCCD and set it's value to 0x01 in the API function?

Then how to actually read the notifications?

0 Likes
1 Solution
Anonymous
Not applicable

           

For enabling notifications you have to write 0x01 to the CCCD of attributehandle of peripheral as shown below.

   data=0x01;

  writeReqParam.attrHandle=0x000F;//Att handle of CCCD

writeReqParam.value.val=&data;

CyBle_GattcWriteCharacteristicDescriptors(cyBle_connHandle, &writeReqParam);

View solution in original post

0 Likes
18 Replies
Anonymous
Not applicable

           

For enabling notifications you have to write 0x01 to the CCCD of attributehandle of peripheral as shown below.

   data=0x01;

  writeReqParam.attrHandle=0x000F;//Att handle of CCCD

writeReqParam.value.val=&data;

CyBle_GattcWriteCharacteristicDescriptors(cyBle_connHandle, &writeReqParam);

0 Likes
Anonymous
Not applicable

resh​ is correct;

Some things to note:

The CCCD value contains bits/flags for enabling/disabling notifications, indications, and possible some other configuration settings.

The peripheral can technically still send notifications when this flag is disabled, but the software should be written to check this value and only send the value if enabled (for compliancy/interoperability when different clients connect to the peripheral).

The notifications will occur as a BLE callback event CYBLE_EVT_GATTC_HANDLE_VALUE_NTF with the associated notification data object being pointed to by the eventParams

0 Likes
Anonymous
Not applicable

Hi guys, thank you so much, just what I needed something clear and concise. However not terribly strong with C++ and got a few errors:

Added uint8 for the first line: uint8 data=0x01;

Then added: extern CYBLE_GATTC_WRITE_REQ_T * writeReqParam;

Before main() but still not right because still getting “->” error for the next two lines:

writeReqParam.attrHandle=0x000F; //Att handle of CCCD

writeReqParam.value.val=&data;

and a “!” warning for the last line:

CyBle_GattcWriteCharacteristicDescriptors(cyBle_connHandle, &writeReqParam);

Am sure I have something wrong with the syntax.

Thanks,

Mark

Sent from Mail for Windows 10

0 Likes
Anonymous
Not applicable

Change the line:

extern CYBLE_GATTC_WRITE_REQ_T * writeReqParam;

to

extern CYBLE_GATTC_WRITE_REQ_T writeReqParam;

extern means that you have the line:

CYBLE_GATTC_WRITE_REQ_T writeReqParam;

(the declaration) in a different file that you want to use/refer to, and the compiler should assume it is there until you compile the program.

The asterisk (*) means that writeReqParam is a pointer, BUT when you pass the variable as &writeReqParam to the function call, you are passing a pointer of that variable. This means that you are passing a pointer to a pointer to an extern declared variable called writeReqParam (which is not what the function wants passed to it). Therefore, make sure you have the declaration without the asterisk, otherwise you are passing the function a pointer to a pointer when it wants a pointer to a variable (the CYBLE_GATTC_WRITE_REQ_T var to be precise).

0 Likes
Anonymous
Not applicable

That is great, thank you so much. You pointed me in the right direction for reading the notification, can I just bounce that off you?

I put this before main():

/* 'proximityValue' stores the proximity value read from CapSense component */

uint8 proximityValue;

// to read notifications

CYBLE_GATTC_HANDLE_VALUE_NTF_PARAM_T * eventParam;

Then this in main() to actually read the value:

//CYBLE_EVT_GATTC_HANDLE_VALUE_NTF

CYBLE_GATTC_HANDLE_VALUE_NTF_PARAM_T *DataNotification=(CYBLE_GATTC_HANDLE_VALUE_NTF_PARAM_T *)eventParam;

proximityValue= *DataNotification->handleValPair.value.val;

It compiles without error but that probably doesn’t mean much, lol.

Thanks again.

Mark O.

Sent from Mail for Windows 10

0 Likes
Anonymous
Not applicable

To read the value, you will want to do:

proximityvalue = Datanotification->handleValPair.value.val;

The asterisk in the front is unecessary, as in c++ that will dereference a pointer. But, the -> will dereference a pointer while accessing a value from the object pointed to (basically doing both steps at the same time). This means that you would be looking at the object pointed to by the pointer which is pointed to by the DataNotification pointer.

In C++, *PointerValue.ValueAccess is equivalent to PointerValue->ValueAccess just with slightly different syntax (and possible slightly different compilation/execution, but essentially the same affect).

0 Likes
Anonymous
Not applicable

Before getting to reading the data, afraid I am still stuck on enabling notifications. I get this error back from the call to :

apiResult=CyBle_GattcWriteCharacteristicDescriptors(cyBle_connHandle, &writeReqParam);

CYBLE_ERROR_INVALID_PARAMETER 'connHandle' value does not represent any existing entry in the Stack

Sent from Mail for Windows 10

0 Likes
Anonymous
Not applicable

The connHandle refers to writing the notification settings to the peripheral from the client; This requires you to be connected with bluetooth to the device. If you are trying to call that function without being connected to the peripheral, then it will throw the error you are seeing.

Connect to the peripheral over bluetooth first, then call the writecharacteristicdescriptors to set the notification settings.

0 Likes
Anonymous
Not applicable

I had assumed establishing a connection would be a prerequisite to passing parameters and data between two BLE devices. The blue status LED on the PSoC_4_BLE_Central_IAS Pioneer board goes from flashing to solid while the status LED on the PSoC_4_BLE_CapSense_Proximity module (powered separately) goes from flashing to off which according to both sets of documentation indicates both are connected. As a test I reset (button) the PSoC_4_BLE_CapSense_Proximity module after they were connected and observed the blue LED on the PSoC_4_BLE_Central_IAS Pioneer board go out.

Also tried both connHandle and cyBle_connHandle in the function argument with the same results.

Really appreciate your help and patience. Will continue to investigate.

Sent from Mail for Windows 10

0 Likes
Anonymous
Not applicable

Have you discovered the services/characteristics on the device yet? It could be it wants you to discover first? (This shouldn't be a requirement in theory)

cyBle_connHandle should be initialized to the correct value after the CYBLE_EVT_GATT_CONNECT_IND event occurs.

Possibly try settings a small timer after connection to see if you are just attempting it too soon? There could be a different event for the BLE connection process that needs to finish before setting the notification enabled.

0 Likes
Anonymous
Not applicable

I am working from the PSoC_4_BLE_Central_IAS example and it does look like it does a discovery. I moved where I am trying to enable notifications to after the discovery call function. Also set a timer before where I originally had the call. Does it matter where the call to these API functions go? I am putting them in main.c should they go in the ApplicationEventHandler() instead?

Sent from Mail for Windows 10

0 Likes
Anonymous
Not applicable

You should be able to call the BLE functions from main.c perfectly fine.

Just to be clear: You are trying to send a notification from the peripheral to the client device using the notification function, but you want the client to send an enable-notification value to the CCCD on the peripheral first.

Since you are using the PSoC_4_BLE_Central_IAS, why not wait until the flag deviceConnected becomes true before sending the notification enable write characteristic request?

Create a flag for "notifications not enabled", then set it to true after you attempt to write the notifications, and just call the notification characteristic write in main based on the flag being true. Ex:

if(notificationsNotEnabled)

{

  SendNotification(); //send the notification characteristic/enable value to the remote device

  notificationsNotEnabled = false; //set this to true when you receive the device connected event or after discovery is complete and notifications are not enabled? Or, put this entire check inside of the if(deviceConnected){} if statement.

}

Perhaps, try creating a custom central device from barebones and set it up to enable the notifications/write the value on connection? That way you can verify it is not something with the way you are calling the API, but rather something with the way the application overall is behaving for the IAS example you are using.

0 Likes
Anonymous
Not applicable

Just to be clear: You are trying to send a notification from the peripheral to the client device using the notification function, but you want the client to send an enable-notification value to the CCCD on the peripheral first.

Yes, but only because that was my understanding of the handshake between client and server. If I can enable notifications directly at the server side that would work as well. I just need the server to stream data.

Since you are using the PSoC_4_BLE_Central_IAS, why not wait until the flag deviceConnected becomes true before sending the notification enable write characteristic request?

Precisely. I was doing at the end of:

if(peripheralFound)

{

Connect to pheripheral.

/* Reset flag to prevent resending the Connect command */

peripheralFound = FALSE;

I put the enable notifications here.

}

Because I thought that would be after the devices had connected and I could use the peripheralFound flag to act as you suggest.

But I also tried it in:

if(deviceConnected)

{

In here.

}

As you suggested insure the connection had been made. But I still got that error. It was being called continuously there though because I had not implemented the flag as you suggest. I agree that would probably be the better place for it. I will move it back to here and add the flag as that seems cleaner.

Right now I am trying to verify that the connHandle has the correct value.

Sent from Mail for Windows 10

0 Likes
Anonymous
Not applicable

If you can watch the connHandle value in real time with a debugger, that would be useful to see;

You should be able to set the default value for the notification enable under the BLE component at the TopDesign.cysch page:

NotificationEnablePicture.PNG

Hmm, I just realized that my own code is using the value cyBle_connHandle.bdHandle for the reference to the device when calling the BLE apis. Try looking at the connHandle data structure and see if you need to pass the connHandle.bdHandle member rather than the connHandle data structure.

0 Likes
Anonymous
Not applicable

I did what we discussed and moved the notification enable code to the if(deviceConnected) section of main in the PSoC_4_BLE_Central_IAS code example I have been working with and added the flag you suggested. The apiResult is now CYBLE_ERROR_OK (yes!!!). So now back to actually reading the notification data. I am also using the UART_to_BLE_central example for reference and notice they put a CYBLE_EVT_GATTC_HANDLE_VALUE_NTF “case” in the call back function to read the notification data so I added the same case statement to ApplicationEventHandler() in my IAS example assuming when the central receives a notification event this case should be activated. However it never seems to get to that case ( I have a uart link to the PC for debugging and put a print statement in that case).

I also went back and tried the cyBle_connHandle instead of connHandle you were just mentioning with the same results, it also gives an apiResult of CYBLE_ERROR_OK after sending the command to enable notifications but does not stop at the above mentioned case statement. I must admit I don’t know the difference between the handle member vs data structure but I am assuming the notification is being enabled in any event now. I can go back and set notifications enabled as the default on the peripheral side as you suggest as well.

It kind of makes sense I should only read the notification data when a new value has been detected at the central side. Just not sure why it is not seeing that “case”.

As always thank you for your continued patience and suggestions.

Sent from Mail for Windows 10

0 Likes
Anonymous
Not applicable

You should be able to set the default value for the notification enable under the BLE component at the TopDesign.cysch page:

I did this on the peripheral side, set notifications for the CapSense characteristic to be enabled in the BLE component but then as a test connected with CySmart and read the CCCD but it was 00:00 same as when notifications were not enabled by default and not 01:00 which I would have expected if the default was in fact changed. I had to manually write to the CCCD again in CySmart to start receiving the data.

Same result at the central side, it connects, says it enabled notifications okay but never stops at case CYBLE_EVT_GATTC_HANDLE_VALUE_NTF: in the ApplicationEventHandler().

Did have a question in the meantime: When do you need to call Cyble_Process_Events()? I see that being called at various places in the examples.

Sent from Mail for Windows 10

0 Likes
Anonymous
Not applicable

Yay! CYBLE_ERROR_OK is always a good sign!

The Cyble_Process_Events() will basically process any pending BLE events; Ideally, you should be running through this when you run through your main code loop. It will take some time depending on how many events happened, but it will return if none are pending, thus saving you having to worry about it blocking if nothing changed. Examples tend to call it after a BLE API call to ensure the API call is acted upon asap. The Cyble_Process_Events() is ultimately the source of the BLE radio being "asynchronous" for operation from the rest of your code.

The Notification Event will not work in the IAS event handler, as the IAS event handler ONLY handles the events designated for IAS characteristics, services, and related events to those. Thus, since you are doing a notification on a custom characteristic, I would try putting the notification event handler inside of the callback that you pass to the Cyble_start() function, rather than the IAS event handler.

I must admit I don’t know the difference between the handle member vs data structure but I am assuming the notification is being enabled in any event now.  I can go back and set notifications enabled as the default on the peripheral side as you suggest as well.
I did this on the peripheral side, set notifications for the CapSense characteristic to be enabled in the BLE component but then as a test connected with CySmart and read the CCCD but it was 00:00 same as when notifications were not enabled by default and not 01:00 which I would have expected if the default was in fact changed.  I had to manually write to the CCCD again in CySmart to start receiving the data.

That is odd; perhaps you are enabling the wrong CCCD? Also, I'm not sure what the difference between the connHandle and the connHandle.BdHandle are, but that was one difference between our programs, so I thought to mention it. I would expect the connHandle on it's own to work 😕 Possibly double check that you are passing the correct variable type: pointer vs direct object, etc.

For the peripheral code, you can easily start by testing the notifications are working first by ignoring checking the notifications are enabled and just sending the notifications regardless. Then, once you have it reliably working to go back and implement the notification enable code to prevent spurious notifications from the application/user perspective.

0 Likes
Anonymous
Not applicable

I don’t pretend to understand any of this, I just look at enough examples and get help from more advanced users like yourself but I am receiving notifications now. Maybe it is still not suppose to work and I just did something that made it work by coincidence but the problem seemed to be the definition of writeReqParam that was the argument to the API call to enable notifications. Looking at the BLE_to_UART_central example I saw they defined it like this:

const uint8 enableNotificationParam[2] = {0x01, 0x00};

/* structure to be passed for the enable notification request */

CYBLE_GATTC_WRITE_REQ_T enableNotification_writeReqParam = {

{(uint8*)enableNotificationParam, 2, 2},

0

};

That seemed to have done the trick.

Think I mentioned in the first post I took out the FindMe profile used in the IAS central example and just duplicated the custom profile from the peripheral side. Also made sure the UUID’s matched. I don’t know if I had to duplicate the CapSence custom service on the central side though. Also took out all references to any IAS functions.

So yes the notification event handler is inside of the callback that is passed to the Cyble_start() function.

I am still puzzled why enabling notifications on the CapSense characteristic on the peripheral side BLE component didn’t seem to do anything. I still had to enable notifications from the client side. I thought notifications should automatically be sent if box is set to enable them by default.

Still A LOT I don’t understand, like on the peripheral side (CapSense Proximity Example) I want to eliminate the need for the user to press a button to wake from sleep and start advertising but when I comment the low power mode #define out, it starts advertising from reset but then won’t send notifications, lol.

Anyway you’ve been a tremendous help, thank you very much.

Sent from Mail for Windows 10

0 Likes