Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Need a way to pass an array of UUIDs #115

Closed
mishafarms opened this issue Oct 6, 2017 · 10 comments
Closed

Need a way to pass an array of UUIDs #115

mishafarms opened this issue Oct 6, 2017 · 10 comments

Comments

@mishafarms
Copy link

mishafarms commented Oct 6, 2017

I am working with your BLE C++ and I am trying to get the advertising packet to include the 16 bit UUIDs complete. I can do this by putting multiple 128-bit UUIDs into an array and pointing the advertising data pointer to it. The only issue I have is that I am not happy with what I did to get to the advertising data pointer (hacked your code). and was wondering if you knew of a better why to accomplish what I want.

Thank you, Michael

Here is my code for a Heart-Rate monitor that connects to Nordic Semi utility App.

Editor: Moved code to pastebin: https://pastebin.com/Ax8Sib1c

@nkolban
Copy link
Owner

nkolban commented Oct 8, 2017

Howdy,
I'd like to clarify what you are asking for. The story, as I understand it, is that you wish to expose a BLE Server running on the ESP32. This server has multiple services that it exposes. Each service has a distinct service UUID. What you would like to do is advertise more than one service in a advertising packet that is published by the ESP32 during each advertisement. Is that correct?

For example ... if your services are UUID 0x0001, UUID 0x0002 and UUID 0x0003 then all of these would be included in the outbound advertisement issued by the ESP32 when it advertises your BLE server.

I wasn't aware that a BLE advertisement could advertise more than one service. By any chance have you examined the BLE specification and determine that this is a valid/lawful thing we can do?

Currently, the BLE C++ implementation is populating an esp_ble_adv_data_t structure ... see:

http://esp-idf.readthedocs.io/en/latest/api-reference/bluetooth/esp_gap_ble.html#_CPPv218esp_ble_adv_data_t

This structure contains the "content" that can be published. If we look deeply at it, we find that it appears to only be able to advertise a single service.

However, I am also aware of another API called esp_ble_gap_config_adv_data_raw that appears to let us own the complete raw data that we are to publish. This could be trivially exposed in the BLE C++ classes but then you would have the requirement to populate it yourself.

My gut is saying that Espressif made publishing advertisements simple through esp_ble_gap_config_adv_data() ... but that is too limiting for your needs. Espressif also provided a "catch all" through esp_ble_gap_config_adv_data_raw() but my suspicion is that is too low level for your needs.

I'm sensing what you would really like is for the BLE C++ classes to take high level requests (i.e. here is a set of 1 or more service UUIDs to advertise) and it worry about how to build a correctly formatted low level advertisement and call esp_ble_gap_config_adv_data_raw(). We can do this ... assuming this is what you want ... but it will take some time.

@mishafarms
Copy link
Author

mishafarms commented Oct 8, 2017

Neil,

In the BLE specification, one of the data types that can be sent is 0x03, This is the complete list of
16 bit UUIDs. As it happens, the Espressif SDK says that p_service_uuid is a service id array point.
If you pass it the full 128 bit UUIDs of the 16 bit UUID in an array and tell it how long the array is,
the advertising packet will advertise the complete 16 bit UUID type with your 16 bit UUIDs included.

This is how the Nordic Semi HR monitor app only shows heart-rate monitor devices. I am not sure if
the SDK will do the same for 32 bit UUIDs, But using this allows me to not have to create the raw
advertising packet by hand for each project I did, and if I add services, I would not have to recreate it.

The code I posted in the first message, does exactly this and creates a heart-rate monitor that the
Nordic App can find. Also I just tested it yesterday with the Qt heart-rate example code, and it works
with it as well (This app don't just display the heart-rate monitors, it displays all BLE devices to connect
to, and if it is a heart-rate monitor it will display the heart-rate)

So what I am looking for is a way to pass the UUID array and have you put it into the p_service_uuid and set the length. Or if you knew of another way to accomplish getting the data type 0x03 into the advertising packet without my having to create a raw advertising packet.

Thanks
Michael

@nkolban
Copy link
Owner

nkolban commented Oct 8, 2017

I'm confused by the advertising API ...

If I supply an array of 8 16 bit UUIDs for services then the service_uuid_len would be 16 (8 * 2) while if I supplied 1 128 bit UUID for the single service, then the service_uuid_len would again be 16 (1 * 16). How does the ESP-IDF interpret whether to send advertising type 0x03 (complete list of 16 bit UUIDs) vs advertising type 0x07 (complete list of 128 bit UUIDs)?

@nkolban
Copy link
Owner

nkolban commented Oct 8, 2017

I have created a ticket for clarification on the subject at the ESP-IDF git issues location:

espressif/esp-idf#1090

@mishafarms
Copy link
Author

mishafarms commented Oct 8, 2017

Neil,

What I pass is an array of 128 bit UUIDs if I pass 2 this arrays length is 32. These 128 UUIDs are the full
UUID of 16 bit UUID services an example is "0000180D-0000-1000-8000-00805F9B34FB" the 16 bit UUID is "180D" which is the SIG assigned heart-rate monitor UUID. the other UUID I used is "0000180F-0000-1000-8000-00805F9B34FB" which is "180F" the SIG assigned battery service.

When I create an array of 32 bytes with these in it and pass it to the ESP API as the p_service_uuid with
32 as the len. The ESP code will advertise the datatype 0x3 automatically, without any other work by me.

This is what I want. I just want a way to do it with your code without my having to hack your code.
I can do it with the method I described. Here is the code snippet.

//	/* try to create 2 UUIDS in a row. */
	BLEUUID *tempUUID = new BLEUUID(HR_SERVICE_UUID128);
	memcpy((void *) serviceUuids[0], (const void *) (tempUUID->getNative()->uuid.uuid128), (size_t) 16);
	tempUUID = new BLEUUID(BATT_SERVICE_UUID128);
	memcpy((void *) serviceUuids[1], (const void *) (tempUUID->getNative()->uuid.uuid128), (size_t) 16);

	pAdvData = pAdvertising->getAdvDataPtr();
	pAdvData->p_service_uuid = &serviceUuids[0][0];
	pAdvData->service_uuid_len = 32;
	pAdvertising->start();

The simple method I added to your BLEAdvertising.h is

	esp_ble_adv_data_t *getAdvDataPtr(void)
	{
	    return &m_advData;
	};

@nkolban
Copy link
Owner

nkolban commented Oct 8, 2017

Howdy my friend,
I'm hearing you say that by providing two 16 byte UUIDs concatenated together in memory and specifying that the length of the data is 32 bytes that you are seeing that you get to advertise two UUIDs of 128bits each. I think that is what you are saying and that you have verified this by empirical evidence and it works.

From my perspective, pretty obviously I could expose an API to achieve just that. However, my thinking is to look at the bigger picture. It seems to me that in the BLE advertising spec, we can advertise lists of UUIDs in an advertisement. The payload of the advertisement specifies:

  1. The size of a UUID (16bit, 32 bit or 128 bit)
  2. An array of UUIDs of the size declared

What I'd thus like to do is create an API that would look something like:

pAdvertising->addUUID(my16BitUUID_1);
pAdvertising->addUUID(my16BitUUID_2);
pAdvertising->addUUID(my16BitUUID_3);
pAdverttising->start();

or could just as easily code:

pAdvertising->addUUID(my128BitUUID_1);
pAdvertising->addUUID(my128BitUUID_2);
pAdverttising->start();

However, achieving this is not as simple as it might appear. We have a mystery in the semantics of the underlying API called esp_ble_gap_config_adv_data. This takes an esp_ble_adv_data_t as input. From what I think you are saying is that if we specify two 128bit UUIDs pointed to b p_service_data with a length of 32 for service_uuid_len we advertise 2 128bit UUIDs. Great!!! However, what if I wanted to advertise 16 16bit UUIDs? That would also be a length of 32 bytes for service_uuid_len and I can't see how the ESP-IDF would distinguish that from 2 128bit UUIDs. What I want to do is consult with Espressif, consult with you, consult with others and try and determine what the right thing to do may be. We have a lower level API called esp_ble_gap_config_adv_data_raw which, if I understand correctly, allows me to drive advertising without ESP-IDF high level wrappers. If I do the job properly, I can achieve our goal by building advertising packets myself in the lower level code and achieve all the goals.

@mishafarms
Copy link
Author

Neil,

It appears you may have misunderstood me. I am passing in 2 128 bit UUIDs, they are the full UUIDs of
The 16 bit UUIDs. I am telling the API that there are 2 128 bit UUIDs by saying the length is 32.
I am not sure if you can pass 16 16 bit UUIDs and claim that the len is also 32 and have the API do
The same thing. But if you passed them 32 128 bit UUIDs that all had 16 bit UUIDs then I assume they
would send out a 0x3 data type with all 32 values.
I do not know what they would do with 32 bit UUIDs (the 128 bit versions) or how they interpret the
the p_service_uuid array. The len is the length of the array not the length of the UUID, a 128 bit UUID is
16 bytes in length. So 32 is the total length of both of my UUIDs.

You could certainly do this by passing in the list of 16 bit UUIDs and creating the raw advertising
packet. That would certainly work and give us the most flexibility.

@nkolban
Copy link
Owner

nkolban commented Oct 8, 2017

If we take a look at this spec page here:

https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile

We see the possible content of advertised payloads. We see that an advertisement is composed of fields and each field is composed of type and data.

Of the types of interest to us, we see the following:

  • 0x03 - Complete list of 16bit UUIDs
  • 0x05 - Complete list of 32bit UUIDs
  • 0x07 - Complete list of 128bit UUIDs

The reason for these distinctions is that an advertising packet can be a maximum of 31 bytes in length. It is quite constrained but has to be in order to fit the data in fixed size radio bursts. If we take this at face value, we immediately see that we actually can't transmit 2 x 128bit UUIDs as an advertised service because that would be 32 bytes and we are constrained to 31 bytes (which includes other overheads).

The question then becomes one of contemplating and comprehending Espressif's thinking on their exposed APIs. My thinking is that they have given us a high level "basic" capability exposing 90% of the things we want to do most of the time (1 service UUID, 1 device name, 1 TX power ... etc). But when we want to do something more advanced, Espressif then gave us a super-low level API for driving the advertising ourselves. The BLE C++ APIs use the high level APIs and I think what needs to be examined is the possibility of driving the low level APIs giving us maximum flexibility. However, to design and code that up will take a little bit of time and, before just jumping on it, I want to check with Espressif (using the Github post to their repository) to make sure that we aren't missing something which would make our story simpler or better.

@nkolban
Copy link
Owner

nkolban commented Oct 11, 2017

The implementation of exposing advertised services from the BLEServer has now been changed. Instead of setServiceUUID() we now have addServiceUUID() as a method on BLEAdvertising. The thinking here is that we can call addServiceUUID() multiple times ... once for each service UUID that we wish to advertise. In the following screen shot, we show the result of a scan where we advertised two 16bit UUID services.

image

Please have a go at testing and let me know if this meets your needs and, if not, how we can change. When you are delighted, go ahead and close out the issue.

nkolban added a commit that referenced this issue Oct 11, 2017
@nkolban nkolban removed the blocked label Oct 11, 2017
@nkolban nkolban self-assigned this Oct 11, 2017
@mishafarms
Copy link
Author

It works great. Thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants