BLE Peripheral Client Reading Data from Android Server

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

cross mob
Anonymous
Not applicable

Hello,

   

 

   

I am using a PSoC 4 kit to output current using an IDAC and BLE to communicate with an Android device. I am operating the PSOC BLE as a GAP Peripheral and a GATT Client. I am attempting to use numerical values that are sent from the Android device in my PSoC Code to output varying current levels. 

   

I am wondering how to properly read the incoming data from the Android device. I understand that in order to write it into the Gatt database, I use CyBle_GattsWriteAttributeValue(), but I'm not sure how to read an incoming value from the Gatt Server. Ideally, this function would constantly be reading the value, and would write a new value using CyBle_GattsWriteAttributeValue() whenever that incoming value changes.

   

Let me know if there is any material online that covers this already. I have searched pretty extensively and have mainly seen how to write into the Gatt Database, and not how to read the value from a Client.

   

Thanks,

   

Davis

0 Likes
1 Solution
Anonymous
Not applicable

To read with the client, you have to connect to the device, initiate discovery (either all services and characteristics, or just the ones you are looking for), and then either read or write using the handles associated with the attributes/characteristics that you "discovered".

   

Check out the API documentation include with the BLE component (go to "TopDesign.cysch", right click the BLE component, choose Open_API_Documentation)

   

The functions of interest should be:

   

CyBle_GattcReadCharacteristicValue()

   

CyBle_GattcWriteCharacteristicValue()

   

CyBle_GattsReadAttributeValue()

   

CyBle_GattsNotification()

   

CyBle_GattsIndication()

   

It sounds like what you are wanting, is to call a function on the PSoC device every time the write attribute function is called? Or are you looking to send the data to the android device?

   

Is the android device setup as a client, a server, or both? 

   

Are you sending data from the android device to the PSoC device using CyBle_Gattcwritecharacteristic()? Or are you wanting to send data from the PSoC chip to the android device using CyBle_GattsNotification()?

   

If you could clarify what you are trying to do, that would be helpful.

View solution in original post

0 Likes
9 Replies
Anonymous
Not applicable

To read with the client, you have to connect to the device, initiate discovery (either all services and characteristics, or just the ones you are looking for), and then either read or write using the handles associated with the attributes/characteristics that you "discovered".

   

Check out the API documentation include with the BLE component (go to "TopDesign.cysch", right click the BLE component, choose Open_API_Documentation)

   

The functions of interest should be:

   

CyBle_GattcReadCharacteristicValue()

   

CyBle_GattcWriteCharacteristicValue()

   

CyBle_GattsReadAttributeValue()

   

CyBle_GattsNotification()

   

CyBle_GattsIndication()

   

It sounds like what you are wanting, is to call a function on the PSoC device every time the write attribute function is called? Or are you looking to send the data to the android device?

   

Is the android device setup as a client, a server, or both? 

   

Are you sending data from the android device to the PSoC device using CyBle_Gattcwritecharacteristic()? Or are you wanting to send data from the PSoC chip to the android device using CyBle_GattsNotification()?

   

If you could clarify what you are trying to do, that would be helpful.

0 Likes
lock attach
Attachments are accessible only for community members.
Anonymous
Not applicable

E.Pratt,

   

Thank you for getting back to me. I've attached my preliminary code below. I have successfully advertised as a Peripheral and connected to it using the CySmart App (My team is still working on the Android application to send the data). It sounds like after the successful connection to the Central device (Android), then I have to begin searching for services that contain data (these will be sent by the Android device). Do you know the proper function to search for services?

   

If this is correct, then I then access the services that are discovered by using their handles. 

   

I am not looking to send information back to the Android device, I am only looking to read (potentially write) information from the Android device. I'm not sure if it is necessary to write the information into the database, or if reading the value is sufficient to then use this read value in my IDAC code. It may be required that I rewrite the value every time it changes in order to use it in the IDAC code. 

   

Thank you for clarifying the original question in regards to the discovery of services. I'm sorry if I'm still a little unclear on the use of the information. I'm hpoing its possible to set up a function to read/write the service value everytime the value changes. When this read/write is triggered, the IDAC code will be triggered again based upon when the value changes.

   

Thanks again, I'll keep you updated with any further updates on my end.

0 Likes
lock attach
Attachments are accessible only for community members.
Anonymous
Not applicable

So, it sounds like: You want to have the peripheral advertise for a mobile app, the mobile app then connects to it as a "server", then peripheral is then setup as the "client", and requests/reads/gets notified of data from the android app when new/updated information becomes available in order to update a hardware DAC that is on the peripheral board.

   

This should be pretty straight forward, and yes there is a "update" function that will do what you are looking for, rather than needing to poll the android database. The two  APIs you should look at are Notification, and Indication; Notification sends data to the "client" when called by the "server", and indication does the same thing, but requires confirmation to assure transmission success (reliability).

   

These two functions are called CyBle_GattsNotification(handle, handle_value_ntf) and CyBle_GattsIndication(handle, handle_value_ntf)

   

As far as discovering services and the characteristics, it is not technically required to search the server to find the handles, but for forward compatibility and interoperability with other devices, it would be a good idea. To search for services, there are multiple ways/apis that will discover various pieces/parts of the server database. For my own application, I used the CyBle_GattcDiscoverPrimaryServiceByUuid(), and passed it the constant uuid value generated by the server (in this case, the android app) to find the service. Once I found the service, I called CyBle_GattcDiscoverAllCharacteristics(), and then decoded and saved the handles corresponding to the characteristics I was interested in. (Calling CyBle_GattcStopCmd() when I'm done discovering the current elements).

   

Once you've read the handles for the characteristics you want (and saved them), then you can just do characteristic reads/writes on the handles to read/write data to the android server.

   

I've attached a modified code version of your main file with an example of:Notifcation handling in the BLE stack handler, as that seems to be what you are looking for. The notification structure received will contain both the value that you are looking to use in the IDAC, and the handle that refers to it on the server, so there shouldn't be a need for discovery of the android server specifically, unless you have multiple characteristics being notified to you.

   

As far as the service and characteristic discovery, it would probably be easier to read the documentation directly for those; Right-click on the BLE component in "TopDesign.cysch", and select "Open API Documentation", then click on "BLE Common APIs", "GATT Functions", "GATT Client Functions"

   

I've attached a pseudocode of an example discovery, but it depends on what you need and how much code you want to write.

   

Here are links to the official cypress examples:

   

http://www.cypress.com/blog/100-projects-100-days

   

They have specific application examples for the most part, but they do explain the general operation/usage of BLE functionality.

Anonymous
Not applicable

E. Pratt,

   

Thank you very much for getting back to me and for getting back to me with such a detailed response. According to your first paragraph, you have a grasp on what I'm trying to do. 

   

Your second paragraph discussing the use of an update function of Notification or Indication is relatable to the Server side of things, correct? The server will be using these functions to relay to the Client that there is notification of data.

   

In terms of accessing this data on the client side, it sounds like there are two primary approaches. One being the use of CyBle_GattcDiscoverPrimaryServiceByUuid() to search for a specific service based upon the UUID on the android side of things. This would allow me to target a specific Service with a known UUID. The second method is to use the response of the servers notification request in the Stack Handler. This is signified by the CYBLE_EVT_GATTC_HANDLE_VALUE_NTF case. When this case occurs, the Handle of notification parameter is stored if it's desired to rewrite into the service, and the value of the parameter is copied into the New Value. Additionally, the Update value determines when the IDAC function should run with an updated value. This value is used later in the for loop to signify when the New Value should be used in the IDAC function.

   

If all above is correct, I believe I have a decent grasp on things. It seems I don't need to be writing into the Gatt DB to access the incoming values like I was attempting to do in my CYBLE_EVT_GATTS_WRITE_REQ case. It's just required to copy the parameters value into a new variable that can later be used.

   

I really appreciate your time and help in talking me through this and providing a bit of code to look at. I'll be sure to post my working code here once completed. 

0 Likes
Anonymous
Not applicable

Correct, the server-side (although, technically both devices can be configured as server devices), will be the one to call the Notification/Indication API, and the client-side will be the one to receive the notification/indication event when it receives the notification/indication from the server-side.

   

Yes, the example I gave does exactly as you described 🙂 Although, everyone learns to "cook" differently as they program, so to speak. I think conceptually, the GATT DB is mostly on the server-side, as you can think of the client as a sort of file reader, or a user. The server is the file itself in analogy.

   

For the application I'm writing for work, I discover the service and characteristics on first connection to a new device, and then reuse/store them for use whenever I want to read/write data to the server. Of course, I have to erase the handles once I unbond or replace the server with a new bond/server, but that is application-specific to what I'm trying to implement.

   

Using either of the two methods in the example I gave you should work ok from what I've seen interacting with the chip 😉

   

The CyBle_GattcReadCharacteristic() would be useful if you are reading the server data asynchronously to the updates to the data on the server, but since you will always want to know the latest data on the client when the server updates the data, using the parameter-handle value to read the newest data, and to write back a "cleared" data should work well. There is the chance that you will run into issues if you end up connected to a device where notifications occur that aren't related to your IDAC, or where there is no "IDAC SERVICE" on the server, but it sounds like that shouldn't be a problem for your case.

   

Glad to be of service, and good luck with the application!

lock attach
Attachments are accessible only for community members.
Anonymous
Not applicable

E. Pratt,

   

I hope you're doing well. I have a quick follow up question to the above project. I have changed gears a little bit and have actually configured my Cypress as the Server and my android as the Client. The android device will be requesting to write into my characteristics. 

   

I have a question regarding the writing and the access of the written characteristic. I have written a write request case in my Callback function that I believe should be configured to write the value into the CYBLE_IRSERVICE_IRDATA characteristic. 

   

Then, in my main function, I am attempting to read the value that was written in, and if the written value is a 1, then I will run the IDAC code pictured.  I do not believe I am reading and accessing the written value correctly. All I need to do is read the value and determine the number. 

   

Let me know if you know of a better approach than what I am attempting.

   

 

   

Thanks,

   

Davis

0 Likes
Anonymous
Not applicable

The callback characteristic write case looks like it should work to me. I agree that the attribute read in main() looks wrong, here is how I would try to rewrite it to make sure the attribute has been written properly:

   

if(IDAC_Update) {
            IDAC_Update = FALSE;//We set this to false so that the IDAC value is only updated when a NEW value is received over BLE,
                                //rather than whenever the unit is awake/running through the main loop
            
            CYBLE_GATT_ERR_CODE_T gattReturn;
            gattReturn = CyBle_GattsReadAttributeValue(&myHandle, &cyBle_connHandle, CYBLE_GATT_DB_LOCALLY_INITIATED);
            if(gattReturn != CYBLE_GATT_ERR_NONE) {
                //Error occurred when program was running, handle as you will 🙂
            } else {
                //No error occurred, update IDAC value
                if(*myHandle.value.val== '1') 
                {
                    isr_1_StartEx(isr_1_handler);
                
                    Timer_1_WritePeriod(PLATEAU_COUNTS);
                    Timer_1_Start();
                    IDAC_Start();
                }
                else{
                    IDAC_Stop();
                    Timer_1_Stop();
                    isr_1_Disable();
                }
            }
        }

   

Part of the issue is that if you do not check the IDAC_Update value first, then you will update the IDAC EVERY time it runs through the main loop code, which could be once (and then the unit goes into deep sleep), or it could be 1000 times (if the unit is getting woken up from deep sleep) each second depending on interrupts, timing, etc.

   

That's why I would only run the code if the flag is set to true, otherwise it will only have the IDAC_Start(); called for 1 cycle through main loop, and then it will stop it.

   

Also, while debugging you can check the return value on the CyBle_GattsReadAttributeValue() function call to see if it was successful or not.

Anonymous
Not applicable

Thank you, that makes sense and was what I was attempting to do. I was actually trying to write a quick troubleshooting code just to make sure I could access the read value first. I was simply going to be running the code if the read value was a 1. My main question I guess is in terms of accessing the *myHandle.value.val value. Is this the correct way to access the read function that is called CyBle_GattsReadAttributeValue(&myHandle, &cyBle_connHandle, CYBLE_GATT_DB_LOCALLY_INITIATED). 

   

Additionally, when relating it to a numerical value, would the way I referenced the pointer equaling the string of a number be correct? 

0 Likes
Anonymous
Not applicable

Yes, and Yes.

   

The pointer you pass into the CyBle_GattsReadAttributeValue() function will get the data written to it by the GattDB. The only way to know if it is a valid value that has been written to the pointer however, is to check the return code of the API call to make sure you aren't using an invalid value.

   

Using the equality operator "==" is fine for comparing the values; Keep in mind however, that comparing 1 == *val is different from comparing "1" == *val, as a string character one is written as the ascii value representing that one, rather than the binary value one.

   

(0x31 versus 0x001)